Compare commits

..

8 Commits

60 changed files with 4009 additions and 2286 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@ -28,7 +28,7 @@ These functions operate on the global `GLOBAL_JSPG` engine instance and provide
* `jspg_setup(database jsonb) -> jsonb`: Initializes the engine. Deserializes the full database schema registry (types, enums, puncs, relations) from Postgres and compiles them into memory atomically. * `jspg_setup(database jsonb) -> jsonb`: Initializes the engine. Deserializes the full database schema registry (types, enums, puncs, relations) from Postgres and compiles them into memory atomically.
* `jspg_teardown() -> jsonb`: Clears the current session's engine instance from `GLOBAL_JSPG`, resetting the cache. * `jspg_teardown() -> jsonb`: Clears the current session's engine instance from `GLOBAL_JSPG`, resetting the cache.
* `jspg_schemas() -> jsonb`: Exports the fully compiled AST snapshot (including all inherited dependencies) out of `GLOBAL_JSPG` into standard JSON Schema representations. * `jspg_database() -> jsonb`: Exports the fully compiled snapshot of the database registry (including Types, Puncs, Enums, and Relations) out of `GLOBAL_JSPG` into standard JSON Schema representations.
--- ---
@ -36,6 +36,17 @@ These functions operate on the global `GLOBAL_JSPG` engine instance and provide
JSPG augments standard JSON Schema 2020-12 to provide an opinionated, strict, and highly ergonomic Object-Oriented paradigm. Developers defining Punc Data Models should follow these conventions. JSPG augments standard JSON Schema 2020-12 to provide an opinionated, strict, and highly ergonomic Object-Oriented paradigm. Developers defining Punc Data Models should follow these conventions.
### Realms (Topological Boundaries)
JSPG strictly organizes schemas into three distinct topological boundaries called **Realms** to prevent cross-contamination and ensure secure API generation:
* **Type Realm (`database.types`)**: Represents physical Postgres tables or structural JSONB bubbles. Table-backed entities here are strictly evaluated for their `type` or `kind` discriminators if they possess polymorphic variations.
* **Punc Realm (`database.puncs`)**: Represents API endpoint Contracts (functions). Contains strictly `.request` and `.response` shapes. These cannot be inherited by standard data models.
* **Enum Realm (`database.enums`)**: Represents simple restricted value lists. Handled universally across all lookups.
The core execution engines natively enforce these boundaries:
* **Validator**: Routes dynamically using a single schema key, transparently switching domains to validate Punc requests/responses from the `Punc` realm, or raw instance payloads from the `Type` realm.
* **Merger**: Strictly bounded to the `Type` Realm. It is philosophically impossible and mathematically illegal to attempt to UPSERT an API endpoint.
* **Queryer**: Routes recursively. Safely evaluates API boundary inputs directly from the `Punc` realm, while tracing underlying table targets back through the `Type` realm to physically compile SQL `SELECT` statements.
### Types of Types ### Types of Types
* **Table-Backed (Entity Types)**: Primarily defined in root `types` schemas. These represent physical Postgres tables. * **Table-Backed (Entity Types)**: Primarily defined in root `types` schemas. These represent physical Postgres tables.
* They are implicitly registered in the Global Registry using their precise key name mapped from the database compilation phase. * They are implicitly registered in the Global Registry using their precise key name mapped from the database compilation phase.
@ -45,12 +56,14 @@ JSPG augments standard JSON Schema 2020-12 to provide an opinionated, strict, an
* **Global Schema Registration**: Roots must be attached to the top-level keys mapped from the `types`, `enums`, or `puncs` database tables. * **Global Schema Registration**: Roots must be attached to the top-level keys mapped from the `types`, `enums`, or `puncs` database tables.
* They can re-use the standard `type` discriminator locally for `oneOf` polymorphism without conflicting with global Postgres Table constraints. * They can re-use the standard `type` discriminator locally for `oneOf` polymorphism without conflicting with global Postgres Table constraints.
### Discriminators & The Dot Convention (A.B) ### Discriminators & The `<Variant>.<Base>` Convention
In Punc, polymorphic targets like explicit tagged unions or STI (Single Table Inheritance) rely on discriminators. Because Punc favors universal consistency, a schema's data contract must be explicit and mathematically identical regardless of the routing context an endpoint consumes it through. In Punc, polymorphic targets like explicit tagged unions or STI (Single Table Inheritance) rely on discriminators. The system heavily leverages a standard `<Variant>.<Base>` dot-notation to enforce topological boundaries deterministically.
**The 2-Tier Paradigm**: The system inherently prevents "God Tables" by restricting routing to exactly two dimensions, guaranteeing absolute $O(1)$ lookups without ambiguity: **The 2-Tier Paradigm**: The system prevents "God Tables" by restricting routing to exactly two dimensions, guaranteeing absolute $O(1)$ lookups without ambiguity:
1. **Vertical Routing (`type`)**: Identifies the specific Postgres Table lineage (e.g. `person` vs `organization`). 1. **Base (Vertical Routing)**: Represents the core physical lineage or foundational structural boundary. For entities, this is the table `type` (e.g. `person` or `widget`). For composed schemas, this is the root structural archetype (e.g., `filter`).
2. **Horizontal Routing (`kind.type`)**: Natively evaluates Single Table Inheritance. The runtime dynamically concatenates `$kind.$type` to yield the namespace-protected schema key (e.g. `light.person`), maintaining collision-free schema registration. 2. **Variant (Horizontal Routing)**: Represents the specific contextual projection or runtime mutation applied to the Base. For STI entities, this is the `kind` (e.g., `light`, `heavy`, `stock`). For composed filters, the variant identifies the entity it targets (e.g., `person`, `invoice`).
When an object is evaluated for STI polymorphism, the runtime natively extracts its `$kind` and `$type` values, dynamically concatenating them as `<Variant>.<Base>` (e.g. `light.person` or `stock.widget`) to yield the namespace-protected schema key.
Therefore, any schema that participates in polymorphic discrimination MUST explicitly define its discriminator properties natively inside its `properties` block. However, to stay DRY and maintain flexible APIs, you **DO NOT** need to hardcode `const` values, nor should you add them to your `required` array. The Punc engine treats `type` and `kind` as **magic properties**. Therefore, any schema that participates in polymorphic discrimination MUST explicitly define its discriminator properties natively inside its `properties` block. However, to stay DRY and maintain flexible APIs, you **DO NOT** need to hardcode `const` values, nor should you add them to your `required` array. The Punc engine treats `type` and `kind` as **magic properties**.
@ -80,6 +93,7 @@ Punc completely abandons the standard JSON Schema `$ref` keyword. Instead, it ov
* **Implicit Keyword Shadowing**: Unlike standard JSON Schema inheritance, local property definitions natively override and shadow inherited properties. * **Implicit Keyword Shadowing**: Unlike standard JSON Schema inheritance, local property definitions natively override and shadow inherited properties.
* **Primitive Array Shorthand (Optionality)**: The `type` array syntax is heavily optimized for nullable fields. Defining `"type": ["budget", "null"]` natively builds a nullable strict, generating `Budget? budget;` in Dart. You can freely mix primitives like `["string", "number", "null"]`. * **Primitive Array Shorthand (Optionality)**: The `type` array syntax is heavily optimized for nullable fields. Defining `"type": ["budget", "null"]` natively builds a nullable strict, generating `Budget? budget;` in Dart. You can freely mix primitives like `["string", "number", "null"]`.
* **Strict Array Constraint**: To explicitly prevent mathematically ambiguous Multiple Inheritance, a `type` array is strictly constrained to at most **ONE** Custom Object Pointer. Defining `"type": ["person", "organization"]` will intentionally trigger a fatal database compilation error natively instructing developers to build a proper tagged union (`oneOf`) instead. * **Strict Array Constraint**: To explicitly prevent mathematically ambiguous Multiple Inheritance, a `type` array is strictly constrained to at most **ONE** Custom Object Pointer. Defining `"type": ["person", "organization"]` will intentionally trigger a fatal database compilation error natively instructing developers to build a proper tagged union (`oneOf`) instead.
* **Dynamic Type Bindings (`"$sibling.[suffix]"`)**: If a `type` string begins with a `$` (e.g., `"type": "$kind.filter"`), the JSPG engine treats it as a Dynamic Pointer. During compile time, it safely defers boundary checks. During runtime validation, the engine dynamically reads the literal string value of the referenced sibling property (`kind`) on the *current parent JSON object*, evaluates the substitution (e.g., `"person.filter"`), and instantly routes execution to that schema in $O(1)$ time. This enables incredibly powerful dynamic JSONB shapes (like a generic `filter` column inside a `search` table) without forcing downstream code generators to build unmaintainable unions.
### Polymorphism (`family` and `oneOf`) ### Polymorphism (`family` and `oneOf`)
Polymorphism is how an object boundary can dynamically take on entirely different shapes based on the payload provided at runtime. Punc utilizes the static database metadata generated from Postgres (`db.types`) to enforce these boundaries deterministically, rather than relying on ambiguous tree-traversals. Polymorphism is how an object boundary can dynamically take on entirely different shapes based on the payload provided at runtime. Punc utilizes the static database metadata generated from Postgres (`db.types`) to enforce these boundaries deterministically, rather than relying on ambiguous tree-traversals.
@ -92,7 +106,7 @@ Polymorphism is how an object boundary can dynamically take on entirely differen
* **Scenario B: Prefixed Tables (Vertical Projection)** * **Scenario B: Prefixed Tables (Vertical Projection)**
* *Setup*: `{ "family": "light.organization" }` * *Setup*: `{ "family": "light.organization" }`
* *Execution*: The engine sees the prefix `light.` and base `organization`. It queries `db.types.get("organization").variations` and dynamically prepends the prefix to discover the relevant UI schemas. * *Execution*: The engine sees the prefix `light.` and base `organization`. It queries `db.types.get("organization").variations` and dynamically prepends the prefix to discover the relevant UI schemas.
* *Options*: `person` -> `light.person`, `organization` -> `light.organization`. (If a projection like `light.bot` does not exist in `db.schemas`, it is safely ignored). * *Options*: `person` -> `light.person`, `organization` -> `light.organization`. (If a projection like `light.bot` does not exist in the Type Registry, it is safely ignored).
* **Scenario C: Single Table Inheritance (Horizontal Routing)** * **Scenario C: Single Table Inheritance (Horizontal Routing)**
* *Setup*: `{ "family": "widget" }` (Where `widget` is a table type but has no external variations). * *Setup*: `{ "family": "widget" }` (Where `widget` is a table type but has no external variations).
* *Execution*: The engine queries `db.types.get("widget").variations` and finds only `["widget"]`. Since it lacks table inheritance, it is treated as STI. The engine scans the specific, confined `schemas` array directly under `db.types.get("widget")` for any registered key terminating in the base `.widget` (e.g., `stock.widget`). The `family` automatically uses `kind` as the discriminator. * *Execution*: The engine queries `db.types.get("widget").variations` and finds only `["widget"]`. Since it lacks table inheritance, it is treated as STI. The engine scans the specific, confined `schemas` array directly under `db.types.get("widget")` for any registered key terminating in the base `.widget` (e.g., `stock.widget`). The `family` automatically uses `kind` as the discriminator.
@ -171,7 +185,7 @@ When compiling nested object graphs or arrays, the JSPG engine must dynamically
### Subschema Promotion ### Subschema Promotion
To seamlessly support deeply nested Object and Array structures, JSPG aggressively promotes them to standalone topological entities during the database compilation phase. To seamlessly support deeply nested Object and Array structures, JSPG aggressively promotes them to standalone topological entities during the database compilation phase.
* **Path Generation:** While evaluating a unified graph originating from a base `types`, `enums`, or `puncs` key, the compiler tracks its exact path descent into nested objects and arrays. It dynamically calculates a localized alias string by appending a `/` pathing syntax (e.g., `base_schema_key/nested/path`) representing exactly its structural constraints. * **Path Generation:** While evaluating a unified graph originating from a base `types`, `enums`, or `puncs` key, the compiler tracks its exact path descent into nested objects and arrays. It dynamically calculates a localized alias string by appending a `/` pathing syntax (e.g., `base_schema_key/nested/path`) representing exactly its structural constraints.
* **Promotion:** This nested subschema chunk is mathematically elevated to its own independent key in the `db.schemas` cache registry using its full path. This guarantees that $O(1)$ WebSockets or isolated queries can natively target any arbitrary nested sub-object of a massive database topology directly without recursively re-parsing its parent's AST block every read. Note that you cannot use the `type` attribute to statically inherit from these automatically promoted subschemas. * **Promotion:** This nested subschema chunk is mathematically elevated to an independent subschema entry natively within its parent's internal scope (e.g. inside `db.types.get("base").schemas`) using its full path. This guarantees that $O(1)$ WebSockets or isolated queries can natively target any arbitrary nested sub-object of a massive database topology directly without recursively re-parsing its parent's AST block every read. Note that you cannot use the `type` attribute to statically inherit from these automatically promoted subschemas.
* **Primitive Confinement:** Purely scalar or primitive branches (like `oneOf: [{type: "string"}, {type: "null"}]`) bypass global topological promotion. They are evaluated directly within the execution engine via isolated Tuple Indexes to explicitly protect the global DB Registry and Go Mixer from memory bloat. * **Primitive Confinement:** Purely scalar or primitive branches (like `oneOf: [{type: "string"}, {type: "null"}]`) bypass global topological promotion. They are evaluated directly within the execution engine via isolated Tuple Indexes to explicitly protect the global DB Registry and Go Mixer from memory bloat.
--- ---

View File

@ -2,21 +2,26 @@
{ {
"description": "additionalProperties validates properties not matched by properties", "description": "additionalProperties validates properties not matched by properties",
"database": { "database": {
"schemas": { "types": [
"schema1": { {
"properties": { "name": "schema1",
"foo": { "schemas": {
"type": "string" "schema1": {
}, "properties": {
"bar": { "foo": {
"type": "number" "type": "string"
},
"bar": {
"type": "number"
}
},
"additionalProperties": {
"type": "boolean"
}
} }
},
"additionalProperties": {
"type": "boolean"
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -61,19 +66,24 @@
{ {
"description": "extensible: true with additionalProperties still validates structure", "description": "extensible: true with additionalProperties still validates structure",
"database": { "database": {
"schemas": { "types": [
"additionalProperties_1_0": { {
"properties": { "name": "additionalProperties_1_0",
"foo": { "schemas": {
"type": "string" "additionalProperties_1_0": {
"properties": {
"foo": {
"type": "string"
}
},
"extensible": true,
"additionalProperties": {
"type": "integer"
}
} }
},
"extensible": true,
"additionalProperties": {
"type": "integer"
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -106,21 +116,26 @@
{ {
"description": "complex additionalProperties with object and array items", "description": "complex additionalProperties with object and array items",
"database": { "database": {
"schemas": { "types": [
"schema3": { {
"properties": { "name": "schema3",
"type": { "schemas": {
"type": "string" "schema3": {
} "properties": {
}, "type": {
"additionalProperties": { "type": "string"
"type": "array", }
"items": { },
"type": "string" "additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,9 +2,14 @@
{ {
"description": "boolean schema 'true'", "description": "boolean schema 'true'",
"database": { "database": {
"schemas": { "types": [
"booleanSchema_0_0": {} {
} "name": "booleanSchema_0_0",
"schemas": {
"booleanSchema_0_0": {}
}
}
]
}, },
"tests": [ "tests": [
{ {
@ -97,11 +102,16 @@
{ {
"description": "boolean schema 'false'", "description": "boolean schema 'false'",
"database": { "database": {
"schemas": { "types": [
"booleanSchema_1_0": { {
"not": {} "name": "booleanSchema_1_0",
"schemas": {
"booleanSchema_1_0": {
"not": {}
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,218 +2,238 @@
{ {
"description": "Multi-Paradigm Declarative Cases", "description": "Multi-Paradigm Declarative Cases",
"database": { "database": {
"schemas": { "types": [
"parallel_rules": { {
"type": "object", "name": "parallel_rules",
"properties": { "schemas": {
"status": { "parallel_rules": {
"type": "string" "type": "object",
}, "properties": {
"kind": { "status": {
"type": "string" "type": "string"
}
},
"cases": [
{
"when": {
"properties": {
"status": {
"const": "unverified"
}
}, },
"required": [ "kind": {
"status" "type": "string"
] }
}, },
"then": { "cases": [
"properties": { {
"amount_1": { "when": {
"type": "number" "properties": {
"status": {
"const": "unverified"
}
},
"required": [
"status"
]
}, },
"amount_2": { "then": {
"type": "number" "properties": {
} "amount_1": {
}, "type": "number"
"required": [
"amount_1",
"amount_2"
]
}
},
{
"when": {
"properties": {
"kind": {
"const": "credit"
}
},
"required": [
"kind"
]
},
"then": {
"properties": {
"cvv": {
"type": "number"
}
},
"required": [
"cvv"
]
}
}
]
},
"mutually_exclusive": {
"type": "object",
"properties": {
"type": {
"type": "string"
}
},
"cases": [
{
"when": {
"properties": {
"type": {
"const": "A"
}
},
"required": [
"type"
]
},
"then": {
"properties": {
"field_a": {
"type": "number"
}
},
"required": [
"field_a"
]
}
},
{
"when": {
"properties": {
"type": {
"const": "B"
}
},
"required": [
"type"
]
},
"then": {
"properties": {
"field_b": {
"type": "number"
}
},
"required": [
"field_b"
]
},
"else": {
"properties": {
"fallback_b": {
"type": "number"
}
},
"required": [
"fallback_b"
]
}
}
]
},
"nested_fallbacks": {
"type": "object",
"properties": {
"tier": {
"type": "string"
}
},
"cases": [
{
"when": {
"properties": {
"tier": {
"const": "1"
}
},
"required": [
"tier"
]
},
"then": {
"properties": {
"basic": {
"type": "number"
}
},
"required": [
"basic"
]
},
"else": {
"cases": [
{
"when": {
"properties": {
"tier": {
"const": "2"
}
}, },
"required": [ "amount_2": {
"tier" "type": "number"
] }
}, },
"then": { "required": [
"properties": { "amount_1",
"standard": { "amount_2"
"type": "number" ]
}
},
"required": [
"standard"
]
},
"else": {
"properties": {
"premium": {
"type": "number"
}
},
"required": [
"premium"
]
}
}
]
}
}
]
},
"missing_when": {
"type": "object",
"cases": [
{
"else": {
"properties": {
"unconditional": {
"type": "number"
} }
}, },
"required": [ {
"unconditional" "when": {
] "properties": {
} "kind": {
"const": "credit"
}
},
"required": [
"kind"
]
},
"then": {
"properties": {
"cvv": {
"type": "number"
}
},
"required": [
"cvv"
]
}
}
]
} }
] }
},
{
"name": "mutually_exclusive",
"schemas": {
"mutually_exclusive": {
"type": "object",
"properties": {
"type": {
"type": "string"
}
},
"cases": [
{
"when": {
"properties": {
"type": {
"const": "A"
}
},
"required": [
"type"
]
},
"then": {
"properties": {
"field_a": {
"type": "number"
}
},
"required": [
"field_a"
]
}
},
{
"when": {
"properties": {
"type": {
"const": "B"
}
},
"required": [
"type"
]
},
"then": {
"properties": {
"field_b": {
"type": "number"
}
},
"required": [
"field_b"
]
},
"else": {
"properties": {
"fallback_b": {
"type": "number"
}
},
"required": [
"fallback_b"
]
}
}
]
}
}
},
{
"name": "nested_fallbacks",
"schemas": {
"nested_fallbacks": {
"type": "object",
"properties": {
"tier": {
"type": "string"
}
},
"cases": [
{
"when": {
"properties": {
"tier": {
"const": "1"
}
},
"required": [
"tier"
]
},
"then": {
"properties": {
"basic": {
"type": "number"
}
},
"required": [
"basic"
]
},
"else": {
"cases": [
{
"when": {
"properties": {
"tier": {
"const": "2"
}
},
"required": [
"tier"
]
},
"then": {
"properties": {
"standard": {
"type": "number"
}
},
"required": [
"standard"
]
},
"else": {
"properties": {
"premium": {
"type": "number"
}
},
"required": [
"premium"
]
}
}
]
}
}
]
}
}
},
{
"name": "missing_when",
"schemas": {
"missing_when": {
"type": "object",
"cases": [
{
"else": {
"properties": {
"unconditional": {
"type": "number"
}
},
"required": [
"unconditional"
]
}
}
]
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,11 +2,16 @@
{ {
"description": "const validation", "description": "const validation",
"database": { "database": {
"schemas": { "types": [
"const_0_0": { {
"const": 2 "name": "const_0_0",
"schemas": {
"const_0_0": {
"const": 2
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -41,18 +46,23 @@
{ {
"description": "const with object", "description": "const with object",
"database": { "database": {
"schemas": { "types": [
"const_1_0": { {
"const": { "name": "const_1_0",
"foo": "bar", "schemas": {
"baz": "bax" "const_1_0": {
}, "const": {
"properties": { "foo": "bar",
"foo": {}, "baz": "bax"
"baz": {} },
"properties": {
"foo": {},
"baz": {}
}
}
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -107,15 +117,20 @@
{ {
"description": "const with array", "description": "const with array",
"database": { "database": {
"schemas": { "types": [
"const_2_0": { {
"const": [ "name": "const_2_0",
{ "schemas": {
"foo": "bar" "const_2_0": {
"const": [
{
"foo": "bar"
}
]
} }
] }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -160,11 +175,16 @@
{ {
"description": "const with null", "description": "const with null",
"database": { "database": {
"schemas": { "types": [
"const_3_0": { {
"const": null "name": "const_3_0",
"schemas": {
"const_3_0": {
"const": null
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -190,11 +210,16 @@
{ {
"description": "const with false does not match 0", "description": "const with false does not match 0",
"database": { "database": {
"schemas": { "types": [
"const_4_0": { {
"const": false "name": "const_4_0",
"schemas": {
"const_4_0": {
"const": false
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -229,11 +254,16 @@
{ {
"description": "const with true does not match 1", "description": "const with true does not match 1",
"database": { "database": {
"schemas": { "types": [
"const_5_0": { {
"const": true "name": "const_5_0",
"schemas": {
"const_5_0": {
"const": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -268,13 +298,18 @@
{ {
"description": "const with [false] does not match [0]", "description": "const with [false] does not match [0]",
"database": { "database": {
"schemas": { "types": [
"const_6_0": { {
"const": [ "name": "const_6_0",
false "schemas": {
] "const_6_0": {
"const": [
false
]
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -315,13 +350,18 @@
{ {
"description": "const with [true] does not match [1]", "description": "const with [true] does not match [1]",
"database": { "database": {
"schemas": { "types": [
"const_7_0": { {
"const": [ "name": "const_7_0",
true "schemas": {
] "const_7_0": {
"const": [
true
]
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -362,13 +402,18 @@
{ {
"description": "const with {\"a\": false} does not match {\"a\": 0}", "description": "const with {\"a\": false} does not match {\"a\": 0}",
"database": { "database": {
"schemas": { "types": [
"const_8_0": { {
"const": { "name": "const_8_0",
"a": false "schemas": {
"const_8_0": {
"const": {
"a": false
}
}
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -409,13 +454,18 @@
{ {
"description": "const with {\"a\": true} does not match {\"a\": 1}", "description": "const with {\"a\": true} does not match {\"a\": 1}",
"database": { "database": {
"schemas": { "types": [
"const_9_0": { {
"const": { "name": "const_9_0",
"a": true "schemas": {
"const_9_0": {
"const": {
"a": true
}
}
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -456,11 +506,16 @@
{ {
"description": "const with 0 does not match other zero-like types", "description": "const with 0 does not match other zero-like types",
"database": { "database": {
"schemas": { "types": [
"const_10_0": { {
"const": 0 "name": "const_10_0",
"schemas": {
"const_10_0": {
"const": 0
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -522,11 +577,16 @@
{ {
"description": "const with 1 does not match true", "description": "const with 1 does not match true",
"database": { "database": {
"schemas": { "types": [
"const_11_0": { {
"const": 1 "name": "const_11_0",
"schemas": {
"const_11_0": {
"const": 1
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -561,11 +621,16 @@
{ {
"description": "const with -2.0 matches integer and float types", "description": "const with -2.0 matches integer and float types",
"database": { "database": {
"schemas": { "types": [
"const_12_0": { {
"const": -2 "name": "const_12_0",
"schemas": {
"const_12_0": {
"const": -2
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -618,11 +683,16 @@
{ {
"description": "float and integers are equal up to 64-bit representation limits", "description": "float and integers are equal up to 64-bit representation limits",
"database": { "database": {
"schemas": { "types": [
"const_13_0": { {
"const": 9007199254740992 "name": "const_13_0",
"schemas": {
"const_13_0": {
"const": 9007199254740992
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -666,11 +736,16 @@
{ {
"description": "nul characters in strings", "description": "nul characters in strings",
"database": { "database": {
"schemas": { "types": [
"const_14_0": { {
"const": "hello\u0000there" "name": "const_14_0",
"schemas": {
"const_14_0": {
"const": "hello\u0000there"
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -696,17 +771,22 @@
{ {
"description": "characters with the same visual representation but different codepoint", "description": "characters with the same visual representation but different codepoint",
"database": { "database": {
"schemas": { "types": [
"const_15_0": { {
"const": "μ", "name": "const_15_0",
"$comment": "U+03BC" "schemas": {
"const_15_0": {
"const": "\u03bc",
"$comment": "U+03BC"
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
"description": "character uses the same codepoint", "description": "character uses the same codepoint",
"data": "μ", "data": "\u03bc",
"comment": "U+03BC", "comment": "U+03BC",
"schema_id": "const_15_0", "schema_id": "const_15_0",
"action": "validate", "action": "validate",
@ -716,7 +796,7 @@
}, },
{ {
"description": "character looks the same but uses a different codepoint", "description": "character looks the same but uses a different codepoint",
"data": "µ", "data": "\u00b5",
"comment": "U+00B5", "comment": "U+00B5",
"schema_id": "const_15_0", "schema_id": "const_15_0",
"action": "validate", "action": "validate",
@ -729,17 +809,22 @@
{ {
"description": "characters with the same visual representation, but different number of codepoints", "description": "characters with the same visual representation, but different number of codepoints",
"database": { "database": {
"schemas": { "types": [
"const_16_0": { {
"const": "ä", "name": "const_16_0",
"$comment": "U+00E4" "schemas": {
"const_16_0": {
"const": "\u00e4",
"$comment": "U+00E4"
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
"description": "character uses the same codepoint", "description": "character uses the same codepoint",
"data": "ä", "data": "\u00e4",
"comment": "U+00E4", "comment": "U+00E4",
"schema_id": "const_16_0", "schema_id": "const_16_0",
"action": "validate", "action": "validate",
@ -749,7 +834,7 @@
}, },
{ {
"description": "character looks the same but uses combining marks", "description": "character looks the same but uses combining marks",
"data": "ä", "data": "a\u0308",
"comment": "a, U+0308", "comment": "a, U+0308",
"schema_id": "const_16_0", "schema_id": "const_16_0",
"action": "validate", "action": "validate",
@ -762,14 +847,19 @@
{ {
"description": "extensible: true allows extra properties in const object match", "description": "extensible: true allows extra properties in const object match",
"database": { "database": {
"schemas": { "types": [
"const_17_0": { {
"const": { "name": "const_17_0",
"a": 1 "schemas": {
}, "const_17_0": {
"extensible": true "const": {
"a": 1
},
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,14 +2,19 @@
{ {
"description": "contains keyword validation", "description": "contains keyword validation",
"database": { "database": {
"schemas": { "types": [
"contains_0_0": { {
"contains": { "name": "contains_0_0",
"minimum": 5 "schemas": {
}, "contains_0_0": {
"items": true "contains": {
"minimum": 5
},
"items": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -88,14 +93,19 @@
{ {
"description": "contains keyword with const keyword", "description": "contains keyword with const keyword",
"database": { "database": {
"schemas": { "types": [
"contains_1_0": { {
"contains": { "name": "contains_1_0",
"const": 5 "schemas": {
}, "contains_1_0": {
"items": true "contains": {
"const": 5
},
"items": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -144,11 +154,16 @@
{ {
"description": "contains keyword with boolean schema true", "description": "contains keyword with boolean schema true",
"database": { "database": {
"schemas": { "types": [
"contains_2_0": { {
"contains": true "name": "contains_2_0",
"schemas": {
"contains_2_0": {
"contains": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -176,11 +191,16 @@
{ {
"description": "contains keyword with boolean schema false", "description": "contains keyword with boolean schema false",
"database": { "database": {
"schemas": { "types": [
"contains_3_0": { {
"contains": false "name": "contains_3_0",
"schemas": {
"contains_3_0": {
"contains": false
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -217,16 +237,21 @@
{ {
"description": "items + contains", "description": "items + contains",
"database": { "database": {
"schemas": { "types": [
"contains_4_0": { {
"items": { "name": "contains_4_0",
"multipleOf": 2 "schemas": {
}, "contains_4_0": {
"contains": { "items": {
"multipleOf": 3 "multipleOf": 2
},
"contains": {
"multipleOf": 3
}
}
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -284,14 +309,19 @@
{ {
"description": "contains with false if subschema", "description": "contains with false if subschema",
"database": { "database": {
"schemas": { "types": [
"contains_5_0": { {
"contains": { "name": "contains_5_0",
"if": false, "schemas": {
"else": true "contains_5_0": {
"contains": {
"if": false,
"else": true
}
}
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -319,13 +349,18 @@
{ {
"description": "contains with null instance elements", "description": "contains with null instance elements",
"database": { "database": {
"schemas": { "types": [
"contains_6_0": { {
"contains": { "name": "contains_6_0",
"type": "null" "schemas": {
"contains_6_0": {
"contains": {
"type": "null"
}
}
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -344,14 +379,19 @@
{ {
"description": "extensible: true allows non-matching items in contains", "description": "extensible: true allows non-matching items in contains",
"database": { "database": {
"schemas": { "types": [
"contains_7_0": { {
"contains": { "name": "contains_7_0",
"const": 1 "schemas": {
}, "contains_7_0": {
"extensible": true "contains": {
"const": 1
},
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -371,13 +411,18 @@
{ {
"description": "strict by default: non-matching items in contains are invalid", "description": "strict by default: non-matching items in contains are invalid",
"database": { "database": {
"schemas": { "types": [
"contains_8_0": { {
"contains": { "name": "contains_8_0",
"const": 1 "schemas": {
"contains_8_0": {
"contains": {
"const": 1
}
}
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,11 +2,16 @@
{ {
"description": "validation of string-encoded content based on media type", "description": "validation of string-encoded content based on media type",
"database": { "database": {
"schemas": { "types": [
"content_0_0": { {
"contentMediaType": "application/json" "name": "content_0_0",
"schemas": {
"content_0_0": {
"contentMediaType": "application/json"
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -41,11 +46,16 @@
{ {
"description": "validation of binary string-encoding", "description": "validation of binary string-encoding",
"database": { "database": {
"schemas": { "types": [
"content_1_0": { {
"contentEncoding": "base64" "name": "content_1_0",
"schemas": {
"content_1_0": {
"contentEncoding": "base64"
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -80,12 +90,17 @@
{ {
"description": "validation of binary-encoded media type documents", "description": "validation of binary-encoded media type documents",
"database": { "database": {
"schemas": { "types": [
"content_2_0": { {
"contentMediaType": "application/json", "name": "content_2_0",
"contentEncoding": "base64" "schemas": {
"content_2_0": {
"contentMediaType": "application/json",
"contentEncoding": "base64"
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -129,26 +144,31 @@
{ {
"description": "validation of binary-encoded media type documents with schema", "description": "validation of binary-encoded media type documents with schema",
"database": { "database": {
"schemas": { "types": [
"content_3_0": { {
"contentMediaType": "application/json", "name": "content_3_0",
"contentEncoding": "base64", "schemas": {
"contentSchema": { "content_3_0": {
"type": "object", "contentMediaType": "application/json",
"required": [ "contentEncoding": "base64",
"foo" "contentSchema": {
], "type": "object",
"properties": { "required": [
"foo": { "foo"
"type": "string" ],
}, "properties": {
"boo": { "foo": {
"type": "integer" "type": "string"
},
"boo": {
"type": "integer"
}
}
} }
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,17 +2,22 @@
{ {
"description": "single dependency (required)", "description": "single dependency (required)",
"database": { "database": {
"schemas": { "types": [
"schema1": { {
"$schema": "https://json-schema.org/draft/2020-12/schema", "name": "schema1",
"dependencies": { "schemas": {
"bar": [ "schema1": {
"foo" "$schema": "https://json-schema.org/draft/2020-12/schema",
] "dependencies": {
}, "bar": [
"extensible": true "foo"
]
},
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -92,15 +97,20 @@
{ {
"description": "empty dependents", "description": "empty dependents",
"database": { "database": {
"schemas": { "types": [
"schema2": { {
"$schema": "https://json-schema.org/draft/2020-12/schema", "name": "schema2",
"dependencies": { "schemas": {
"bar": [] "schema2": {
}, "$schema": "https://json-schema.org/draft/2020-12/schema",
"extensible": true "dependencies": {
"bar": []
},
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -137,18 +147,23 @@
{ {
"description": "multiple dependents required", "description": "multiple dependents required",
"database": { "database": {
"schemas": { "types": [
"schema3": { {
"$schema": "https://json-schema.org/draft/2020-12/schema", "name": "schema3",
"dependencies": { "schemas": {
"quux": [ "schema3": {
"foo", "$schema": "https://json-schema.org/draft/2020-12/schema",
"bar" "dependencies": {
] "quux": [
}, "foo",
"extensible": true "bar"
]
},
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -225,20 +240,25 @@
{ {
"description": "dependencies with escaped characters", "description": "dependencies with escaped characters",
"database": { "database": {
"schemas": { "types": [
"schema4": { {
"$schema": "https://json-schema.org/draft/2020-12/schema", "name": "schema4",
"dependencies": { "schemas": {
"foo\nbar": [ "schema4": {
"foo\rbar" "$schema": "https://json-schema.org/draft/2020-12/schema",
], "dependencies": {
"foo\"bar": [ "foo\nbar": [
"foo'bar" "foo\rbar"
] ],
}, "foo\"bar": [
"extensible": true "foo'bar"
]
},
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -293,17 +313,22 @@
{ {
"description": "extensible: true allows extra properties in dependentRequired", "description": "extensible: true allows extra properties in dependentRequired",
"database": { "database": {
"schemas": { "types": [
"schema5": { {
"$schema": "https://json-schema.org/draft/2020-12/schema", "name": "schema5",
"dependencies": { "schemas": {
"bar": [ "schema5": {
"foo" "$schema": "https://json-schema.org/draft/2020-12/schema",
] "dependencies": {
}, "bar": [
"extensible": true "foo"
]
},
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -324,27 +349,32 @@
{ {
"description": "single dependency (schemas, STRICT)", "description": "single dependency (schemas, STRICT)",
"database": { "database": {
"schemas": { "types": [
"schema_schema1": { {
"$schema": "https://json-schema.org/draft/2020-12/schema", "name": "schema_schema1",
"properties": { "schemas": {
"foo": true, "schema_schema1": {
"bar": true "$schema": "https://json-schema.org/draft/2020-12/schema",
},
"dependencies": {
"bar": {
"properties": { "properties": {
"foo": { "foo": true,
"type": "integer" "bar": true
}, },
"dependencies": {
"bar": { "bar": {
"type": "integer" "properties": {
"foo": {
"type": "integer"
},
"bar": {
"type": "integer"
}
}
} }
} }
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -445,28 +475,33 @@
{ {
"description": "single dependency (schemas, EXTENSIBLE)", "description": "single dependency (schemas, EXTENSIBLE)",
"database": { "database": {
"schemas": { "types": [
"schema_schema2": { {
"$schema": "https://json-schema.org/draft/2020-12/schema", "name": "schema_schema2",
"properties": { "schemas": {
"foo": true, "schema_schema2": {
"bar": true "$schema": "https://json-schema.org/draft/2020-12/schema",
},
"dependencies": {
"bar": {
"properties": { "properties": {
"foo": { "foo": true,
"type": "integer" "bar": true
}, },
"dependencies": {
"bar": { "bar": {
"type": "integer" "properties": {
"foo": {
"type": "integer"
},
"bar": {
"type": "integer"
}
}
} }
} },
"extensible": true
} }
}, }
"extensible": true
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -485,19 +520,24 @@
{ {
"description": "boolean subschemas", "description": "boolean subschemas",
"database": { "database": {
"schemas": { "types": [
"schema_schema3": { {
"$schema": "https://json-schema.org/draft/2020-12/schema", "name": "schema_schema3",
"properties": { "schemas": {
"foo": true, "schema_schema3": {
"bar": true "$schema": "https://json-schema.org/draft/2020-12/schema",
}, "properties": {
"dependencies": { "foo": true,
"foo": true, "bar": true
"bar": false },
"dependencies": {
"foo": true,
"bar": false
}
}
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -548,29 +588,34 @@
{ {
"description": "dependencies with escaped characters", "description": "dependencies with escaped characters",
"database": { "database": {
"schemas": { "types": [
"schema_schema4": { {
"$schema": "https://json-schema.org/draft/2020-12/schema", "name": "schema_schema4",
"properties": { "schemas": {
"foo\tbar": true, "schema_schema4": {
"foo'bar": true, "$schema": "https://json-schema.org/draft/2020-12/schema",
"a": true, "properties": {
"b": true, "foo\tbar": true,
"c": true "foo'bar": true,
}, "a": true,
"dependencies": { "b": true,
"foo\tbar": { "c": true
"minProperties": 4, },
"extensible": true "dependencies": {
}, "foo\tbar": {
"foo'bar": { "minProperties": 4,
"required": [ "extensible": true
"foo\"bar" },
] "foo'bar": {
"required": [
"foo\"bar"
]
}
}
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -628,22 +673,27 @@
{ {
"description": "dependent subschema incompatible with root (STRICT)", "description": "dependent subschema incompatible with root (STRICT)",
"database": { "database": {
"schemas": { "types": [
"schema_schema5": { {
"$schema": "https://json-schema.org/draft/2020-12/schema", "name": "schema_schema5",
"properties": { "schemas": {
"foo": {}, "schema_schema5": {
"baz": true "$schema": "https://json-schema.org/draft/2020-12/schema",
},
"dependencies": {
"foo": {
"properties": { "properties": {
"bar": {} "foo": {},
"baz": true
},
"dependencies": {
"foo": {
"properties": {
"bar": {}
}
}
} }
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -701,24 +751,29 @@
{ {
"description": "dependent subschema incompatible with root (EXTENSIBLE)", "description": "dependent subschema incompatible with root (EXTENSIBLE)",
"database": { "database": {
"schemas": { "types": [
"schema_schema6": { {
"$schema": "https://json-schema.org/draft/2020-12/schema", "name": "schema_schema6",
"properties": { "schemas": {
"foo": {}, "schema_schema6": {
"baz": true "$schema": "https://json-schema.org/draft/2020-12/schema",
},
"dependencies": {
"foo": {
"properties": { "properties": {
"bar": {} "foo": {},
"baz": true
}, },
"additionalProperties": false "dependencies": {
"foo": {
"properties": {
"bar": {}
},
"additionalProperties": false
}
},
"extensible": true
} }
}, }
"extensible": true
} }
} ]
}, },
"tests": [ "tests": [
{ {

155
fixtures/dynamicType.json Normal file
View File

@ -0,0 +1,155 @@
[
{
"description": "Dynamic type binding ($sibling.suffix) validation",
"database": {
"types": [
{
"name": "person",
"schemas": {
"person.filter": {
"properties": {
"age": {
"type": "integer"
}
}
}
}
},
{
"name": "widget",
"schemas": {
"widget.filter": {
"properties": {
"weight": {
"type": "integer"
}
}
}
}
},
{
"name": "search",
"schemas": {
"search": {
"properties": {
"kind": {
"type": "string"
},
"filter": {
"type": "$kind.filter"
}
}
}
}
}
]
},
"tests": [
{
"description": "Valid person filter payload",
"data": {
"kind": "person",
"filter": {
"age": 30
}
},
"schema_id": "search",
"action": "validate",
"expect": {
"success": true
}
},
{
"description": "Invalid person filter payload (fails constraint)",
"data": {
"kind": "person",
"filter": {
"age": "thirty"
}
},
"schema_id": "search",
"action": "validate",
"expect": {
"success": false,
"errors": [
{
"code": "INVALID_TYPE",
"details": {
"path": "filter/age"
}
}
]
}
},
{
"description": "Valid widget filter payload",
"data": {
"kind": "widget",
"filter": {
"weight": 500
}
},
"schema_id": "search",
"action": "validate",
"expect": {
"success": true
}
},
{
"description": "Fails resolution if kind doesn't match an existing schema",
"data": {
"kind": "unknown",
"filter": {
"weight": 500
}
},
"schema_id": "search",
"action": "validate",
"expect": {
"success": false,
"errors": [
{
"code": "DYNAMIC_TYPE_RESOLUTION_FAILED",
"details": {
"path": "filter"
}
},
{
"code": "STRICT_PROPERTY_VIOLATION",
"details": {
"path": "filter/weight"
}
}
]
}
},
{
"description": "Fails resolution if discriminator is missing",
"data": {
"filter": {
"weight": 500
}
},
"schema_id": "search",
"action": "validate",
"expect": {
"success": false,
"errors": [
{
"code": "DYNAMIC_TYPE_RESOLUTION_FAILED",
"details": {
"path": "filter"
}
},
{
"code": "STRICT_PROPERTY_VIOLATION",
"details": {
"path": "filter/weight"
}
}
]
}
}
]
}
]

View File

@ -2,43 +2,48 @@
{ {
"description": "empty string is valid for all types (except const)", "description": "empty string is valid for all types (except const)",
"database": { "database": {
"schemas": { "types": [
"emptyString_0_0": { {
"properties": { "name": "emptyString_0_0",
"obj": { "schemas": {
"type": "object" "emptyString_0_0": {
}, "properties": {
"arr": { "obj": {
"type": "array" "type": "object"
}, },
"str": { "arr": {
"type": "string" "type": "array"
}, },
"int": { "str": {
"type": "integer" "type": "string"
}, },
"num": { "int": {
"type": "number" "type": "integer"
}, },
"bool": { "num": {
"type": "boolean" "type": "number"
}, },
"nul": { "bool": {
"type": "null" "type": "boolean"
}, },
"fmt": { "nul": {
"type": "string", "type": "null"
"format": "uuid" },
}, "fmt": {
"con": { "type": "string",
"const": "value" "format": "uuid"
}, },
"con_empty": { "con": {
"const": "" "const": "value"
},
"con_empty": {
"const": ""
}
}
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,15 +2,20 @@
{ {
"description": "simple enum validation", "description": "simple enum validation",
"database": { "database": {
"schemas": { "types": [
"enum_0_0": { {
"enum": [ "name": "enum_0_0",
1, "schemas": {
2, "enum_0_0": {
3 "enum": [
] 1,
2,
3
]
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -36,22 +41,27 @@
{ {
"description": "heterogeneous enum validation", "description": "heterogeneous enum validation",
"database": { "database": {
"schemas": { "types": [
"enum_1_0": { {
"enum": [ "name": "enum_1_0",
6, "schemas": {
"foo", "enum_1_0": {
[], "enum": [
true, 6,
{ "foo",
"foo": 12 [],
true,
{
"foo": 12
}
],
"properties": {
"foo": {}
}
} }
],
"properties": {
"foo": {}
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -111,14 +121,19 @@
{ {
"description": "heterogeneous enum-with-null validation", "description": "heterogeneous enum-with-null validation",
"database": { "database": {
"schemas": { "types": [
"enum_2_0": { {
"enum": [ "name": "enum_2_0",
6, "schemas": {
null "enum_2_0": {
] "enum": [
6,
null
]
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -153,26 +168,31 @@
{ {
"description": "enums in properties", "description": "enums in properties",
"database": { "database": {
"schemas": { "types": [
"enum_3_0": { {
"type": "object", "name": "enum_3_0",
"properties": { "schemas": {
"foo": { "enum_3_0": {
"enum": [ "type": "object",
"foo" "properties": {
] "foo": {
}, "enum": [
"bar": { "foo"
"enum": [ ]
},
"bar": {
"enum": [
"bar"
]
}
},
"required": [
"bar" "bar"
] ]
} }
}, }
"required": [
"bar"
]
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -247,14 +267,19 @@
{ {
"description": "enum with escaped characters", "description": "enum with escaped characters",
"database": { "database": {
"schemas": { "types": [
"enum_4_0": { {
"enum": [ "name": "enum_4_0",
"foo\nbar", "schemas": {
"foo\rbar" "enum_4_0": {
] "enum": [
"foo\nbar",
"foo\rbar"
]
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -289,13 +314,18 @@
{ {
"description": "enum with false does not match 0", "description": "enum with false does not match 0",
"database": { "database": {
"schemas": { "types": [
"enum_5_0": { {
"enum": [ "name": "enum_5_0",
false "schemas": {
] "enum_5_0": {
"enum": [
false
]
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -330,15 +360,20 @@
{ {
"description": "enum with [false] does not match [0]", "description": "enum with [false] does not match [0]",
"database": { "database": {
"schemas": { "types": [
"enum_6_0": { {
"enum": [ "name": "enum_6_0",
[ "schemas": {
false "enum_6_0": {
] "enum": [
] [
false
]
]
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -379,13 +414,18 @@
{ {
"description": "enum with true does not match 1", "description": "enum with true does not match 1",
"database": { "database": {
"schemas": { "types": [
"enum_7_0": { {
"enum": [ "name": "enum_7_0",
true "schemas": {
] "enum_7_0": {
"enum": [
true
]
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -420,15 +460,20 @@
{ {
"description": "enum with [true] does not match [1]", "description": "enum with [true] does not match [1]",
"database": { "database": {
"schemas": { "types": [
"enum_8_0": { {
"enum": [ "name": "enum_8_0",
[ "schemas": {
true "enum_8_0": {
] "enum": [
] [
true
]
]
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -469,13 +514,18 @@
{ {
"description": "enum with 0 does not match false", "description": "enum with 0 does not match false",
"database": { "database": {
"schemas": { "types": [
"enum_9_0": { {
"enum": [ "name": "enum_9_0",
0 "schemas": {
] "enum_9_0": {
"enum": [
0
]
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -510,15 +560,20 @@
{ {
"description": "enum with [0] does not match [false]", "description": "enum with [0] does not match [false]",
"database": { "database": {
"schemas": { "types": [
"enum_10_0": { {
"enum": [ "name": "enum_10_0",
[ "schemas": {
0 "enum_10_0": {
] "enum": [
] [
0
]
]
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -559,13 +614,18 @@
{ {
"description": "enum with 1 does not match true", "description": "enum with 1 does not match true",
"database": { "database": {
"schemas": { "types": [
"enum_11_0": { {
"enum": [ "name": "enum_11_0",
1 "schemas": {
] "enum_11_0": {
"enum": [
1
]
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -600,15 +660,20 @@
{ {
"description": "enum with [1] does not match [true]", "description": "enum with [1] does not match [true]",
"database": { "database": {
"schemas": { "types": [
"enum_12_0": { {
"enum": [ "name": "enum_12_0",
[ "schemas": {
1 "enum_12_0": {
] "enum": [
] [
1
]
]
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -649,13 +714,18 @@
{ {
"description": "nul characters in strings", "description": "nul characters in strings",
"database": { "database": {
"schemas": { "types": [
"enum_13_0": { {
"enum": [ "name": "enum_13_0",
"hello\u0000there" "schemas": {
] "enum_13_0": {
"enum": [
"hello\u0000there"
]
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -681,16 +751,21 @@
{ {
"description": "extensible: true allows extra properties in enum object match", "description": "extensible: true allows extra properties in enum object match",
"database": { "database": {
"schemas": { "types": [
"enum_14_0": { {
"enum": [ "name": "enum_14_0",
{ "schemas": {
"foo": 1 "enum_14_0": {
"enum": [
{
"foo": 1
}
],
"extensible": true
} }
], }
"extensible": true
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,11 +2,16 @@
{ {
"description": "exclusiveMaximum validation", "description": "exclusiveMaximum validation",
"database": { "database": {
"schemas": { "types": [
"exclusiveMaximum_0_0": { {
"exclusiveMaximum": 3 "name": "exclusiveMaximum_0_0",
"schemas": {
"exclusiveMaximum_0_0": {
"exclusiveMaximum": 3
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,11 +2,16 @@
{ {
"description": "exclusiveMinimum validation", "description": "exclusiveMinimum validation",
"database": { "database": {
"schemas": { "types": [
"exclusiveMinimum_0_0": { {
"exclusiveMinimum": 1.1 "name": "exclusiveMinimum_0_0",
"schemas": {
"exclusiveMinimum_0_0": {
"exclusiveMinimum": 1.1
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -107,17 +107,17 @@
"search": { "search": {
"type": "object", "type": "object",
"properties": { "properties": {
"kind": {
"type": "string"
},
"name": { "name": {
"type": "string" "type": "string"
}, },
"filter": { "filter": {
"type": "filter" "type": "$kind.filter"
} }
} }
}, },
"filter": {
"type": "object"
},
"condition": { "condition": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -172,14 +172,50 @@
"schemas": { "schemas": {
"person": {}, "person": {},
"person.filter": { "person.filter": {
"type": "filter", "type": "object",
"compiledPropertyNames": [ "compiledPropertyNames": [
"$and",
"$or",
"age", "age",
"billing_address", "billing_address",
"birth_date", "birth_date",
"first_name" "first_name"
], ],
"properties": { "properties": {
"$and": {
"type": [
"array",
"null"
],
"items": {
"compiledPropertyNames": [
"$and",
"$or",
"age",
"billing_address",
"birth_date",
"first_name"
],
"type": "person.filter"
}
},
"$or": {
"type": [
"array",
"null"
],
"items": {
"compiledPropertyNames": [
"$and",
"$or",
"age",
"billing_address",
"birth_date",
"first_name"
],
"type": "person.filter"
}
},
"first_name": { "first_name": {
"type": [ "type": [
"string.condition", "string.condition",
@ -208,11 +244,41 @@
}, },
"address": {}, "address": {},
"address.filter": { "address.filter": {
"type": "filter", "type": "object",
"compiledPropertyNames": [ "compiledPropertyNames": [
"$and",
"$or",
"city" "city"
], ],
"properties": { "properties": {
"$and": {
"type": [
"array",
"null"
],
"items": {
"compiledPropertyNames": [
"$and",
"$or",
"city"
],
"type": "address.filter"
}
},
"$or": {
"type": [
"array",
"null"
],
"items": {
"compiledPropertyNames": [
"$and",
"$or",
"city"
],
"type": "address.filter"
}
},
"city": { "city": {
"type": [ "type": [
"string.condition", "string.condition",
@ -221,22 +287,62 @@
} }
} }
}, },
"filter": {},
"condition": {}, "condition": {},
"string.condition": {}, "string.condition": {},
"integer.condition": {}, "integer.condition": {},
"date.condition": {}, "date.condition": {},
"search": {}, "search": {},
"search.filter": { "search.filter": {
"type": "filter", "type": "object",
"compiledPropertyNames": [ "compiledPropertyNames": [
"$and",
"$or",
"filter", "filter",
"kind",
"name" "name"
], ],
"properties": { "properties": {
"$and": {
"type": [
"array",
"null"
],
"items": {
"compiledPropertyNames": [
"$and",
"$or",
"filter",
"kind",
"name"
],
"type": "search.filter"
}
},
"$or": {
"type": [
"array",
"null"
],
"items": {
"compiledPropertyNames": [
"$and",
"$or",
"filter",
"kind",
"name"
],
"type": "search.filter"
}
},
"filter": { "filter": {
"type": [ "type": [
"filter.filter", "$kind.filter.filter",
"null"
]
},
"kind": {
"type": [
"string.condition",
"null" "null"
] ]
}, },

File diff suppressed because it is too large Load Diff

View File

@ -2,13 +2,18 @@
{ {
"description": "a schema given for items", "description": "a schema given for items",
"database": { "database": {
"schemas": { "types": [
"items_0_0": { {
"items": { "name": "items_0_0",
"type": "integer" "schemas": {
"items_0_0": {
"items": {
"type": "integer"
}
}
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -64,11 +69,16 @@
{ {
"description": "items with boolean schema (true)", "description": "items with boolean schema (true)",
"database": { "database": {
"schemas": { "types": [
"items_1_0": { {
"items": true "name": "items_1_0",
"schemas": {
"items_1_0": {
"items": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -98,11 +108,16 @@
{ {
"description": "items with boolean schema (false)", "description": "items with boolean schema (false)",
"database": { "database": {
"schemas": { "types": [
"items_2_0": { {
"items": false "name": "items_2_0",
"schemas": {
"items_2_0": {
"items": false
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -132,41 +147,56 @@
{ {
"description": "items and subitems", "description": "items and subitems",
"database": { "database": {
"schemas": { "types": [
"items_3_0": { {
"type": "array", "name": "items_3_0",
"items": false, "schemas": {
"prefixItems": [ "items_3_0": {
{ "type": "array",
"type": "item" "items": false,
}, "prefixItems": [
{ {
"type": "item" "type": "item"
}, },
{ {
"type": "item" "type": "item"
},
{
"type": "item"
}
]
} }
] }
}, },
"item": { {
"type": "array", "name": "item",
"items": false, "schemas": {
"prefixItems": [ "item": {
{ "type": "array",
"type": "sub-item" "items": false,
}, "prefixItems": [
{ {
"type": "sub-item" "type": "sub-item"
},
{
"type": "sub-item"
}
]
} }
] }
}, },
"sub-item": { {
"type": "object", "name": "sub-item",
"required": [ "schemas": {
"foo" "sub-item": {
] "type": "object",
"required": [
"foo"
]
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -368,23 +398,28 @@
{ {
"description": "nested items", "description": "nested items",
"database": { "database": {
"schemas": { "types": [
"items_4_0": { {
"type": "array", "name": "items_4_0",
"items": { "schemas": {
"type": "array", "items_4_0": {
"items": {
"type": "array", "type": "array",
"items": { "items": {
"type": "array", "type": "array",
"items": { "items": {
"type": "number" "type": "array",
"items": {
"type": "array",
"items": {
"type": "number"
}
}
} }
} }
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -500,16 +535,21 @@
{ {
"description": "prefixItems with no additional items allowed", "description": "prefixItems with no additional items allowed",
"database": { "database": {
"schemas": { "types": [
"items_5_0": { {
"prefixItems": [ "name": "items_5_0",
{}, "schemas": {
{}, "items_5_0": {
{} "prefixItems": [
], {},
"items": false {},
{}
],
"items": false
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -576,22 +616,27 @@
{ {
"description": "items does not look in applicators, valid case", "description": "items does not look in applicators, valid case",
"database": { "database": {
"schemas": { "types": [
"items_6_0": { {
"allOf": [ "name": "items_6_0",
{ "schemas": {
"prefixItems": [ "items_6_0": {
"allOf": [
{ {
"minimum": 3 "prefixItems": [
{
"minimum": 3
}
]
} }
] ],
"items": {
"minimum": 5
}
} }
],
"items": {
"minimum": 5
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -623,18 +668,23 @@
{ {
"description": "prefixItems validation adjusts the starting index for items", "description": "prefixItems validation adjusts the starting index for items",
"database": { "database": {
"schemas": { "types": [
"items_7_0": { {
"prefixItems": [ "name": "items_7_0",
{ "schemas": {
"type": "string" "items_7_0": {
"prefixItems": [
{
"type": "string"
}
],
"items": {
"type": "integer"
}
} }
],
"items": {
"type": "integer"
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -667,14 +717,19 @@
{ {
"description": "items with heterogeneous array", "description": "items with heterogeneous array",
"database": { "database": {
"schemas": { "types": [
"items_8_0": { {
"prefixItems": [ "name": "items_8_0",
{} "schemas": {
], "items_8_0": {
"items": false "prefixItems": [
{}
],
"items": false
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -706,13 +761,18 @@
{ {
"description": "items with null instance elements", "description": "items with null instance elements",
"database": { "database": {
"schemas": { "types": [
"items_9_0": { {
"items": { "name": "items_9_0",
"type": "null" "schemas": {
"items_9_0": {
"items": {
"type": "null"
}
}
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -731,12 +791,17 @@
{ {
"description": "extensible: true allows extra items (when items is false)", "description": "extensible: true allows extra items (when items is false)",
"database": { "database": {
"schemas": { "types": [
"items_10_0": { {
"items": false, "name": "items_10_0",
"extensible": true "schemas": {
"items_10_0": {
"items": false,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -755,14 +820,19 @@
{ {
"description": "extensible: true allows extra properties for items", "description": "extensible: true allows extra properties for items",
"database": { "database": {
"schemas": { "types": [
"items_11_0": { {
"items": { "name": "items_11_0",
"minimum": 5 "schemas": {
}, "items_11_0": {
"extensible": true "items": {
"minimum": 5
},
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -793,12 +863,17 @@
{ {
"description": "array: simple extensible array", "description": "array: simple extensible array",
"database": { "database": {
"schemas": { "types": [
"items_12_0": { {
"type": "array", "name": "items_12_0",
"extensible": true "schemas": {
"items_12_0": {
"type": "array",
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -827,12 +902,17 @@
{ {
"description": "array: strict array", "description": "array: strict array",
"database": { "database": {
"schemas": { "types": [
"items_13_0": { {
"type": "array", "name": "items_13_0",
"extensible": false "schemas": {
"items_13_0": {
"type": "array",
"extensible": false
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -860,14 +940,19 @@
{ {
"description": "array: items extensible", "description": "array: items extensible",
"database": { "database": {
"schemas": { "types": [
"items_14_0": { {
"type": "array", "name": "items_14_0",
"items": { "schemas": {
"extensible": true "items_14_0": {
"type": "array",
"items": {
"extensible": true
}
}
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -897,15 +982,20 @@
{ {
"description": "array: items strict", "description": "array: items strict",
"database": { "database": {
"schemas": { "types": [
"items_15_0": { {
"type": "array", "name": "items_15_0",
"items": { "schemas": {
"type": "object", "items_15_0": {
"extensible": false "type": "array",
"items": {
"type": "object",
"extensible": false
}
}
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,12 +2,17 @@
{ {
"description": "maxContains without contains is ignored", "description": "maxContains without contains is ignored",
"database": { "database": {
"schemas": { "types": [
"maxContains_0_0": { {
"maxContains": 1, "name": "maxContains_0_0",
"extensible": true "schemas": {
"maxContains_0_0": {
"maxContains": 1,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -38,15 +43,20 @@
{ {
"description": "maxContains with contains", "description": "maxContains with contains",
"database": { "database": {
"schemas": { "types": [
"maxContains_1_0": { {
"contains": { "name": "maxContains_1_0",
"const": 1 "schemas": {
}, "maxContains_1_0": {
"maxContains": 1, "contains": {
"extensible": true "const": 1
},
"maxContains": 1,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -111,15 +121,20 @@
{ {
"description": "maxContains with contains, value with a decimal", "description": "maxContains with contains, value with a decimal",
"database": { "database": {
"schemas": { "types": [
"maxContains_2_0": { {
"contains": { "name": "maxContains_2_0",
"const": 1 "schemas": {
}, "maxContains_2_0": {
"maxContains": 1, "contains": {
"extensible": true "const": 1
},
"maxContains": 1,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -150,16 +165,21 @@
{ {
"description": "minContains < maxContains", "description": "minContains < maxContains",
"database": { "database": {
"schemas": { "types": [
"maxContains_3_0": { {
"contains": { "name": "maxContains_3_0",
"const": 1 "schemas": {
}, "maxContains_3_0": {
"minContains": 1, "contains": {
"maxContains": 3, "const": 1
"extensible": true },
"minContains": 1,
"maxContains": 3,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -202,15 +222,20 @@
{ {
"description": "extensible: true allows non-matching items in maxContains", "description": "extensible: true allows non-matching items in maxContains",
"database": { "database": {
"schemas": { "types": [
"maxContains_4_0": { {
"contains": { "name": "maxContains_4_0",
"const": 1 "schemas": {
}, "maxContains_4_0": {
"maxContains": 1, "contains": {
"extensible": true "const": 1
},
"maxContains": 1,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,12 +2,17 @@
{ {
"description": "maxItems validation", "description": "maxItems validation",
"database": { "database": {
"schemas": { "types": [
"maxItems_0_0": { {
"maxItems": 2, "name": "maxItems_0_0",
"extensible": true "schemas": {
"maxItems_0_0": {
"maxItems": 2,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -60,12 +65,17 @@
{ {
"description": "maxItems validation with a decimal", "description": "maxItems validation with a decimal",
"database": { "database": {
"schemas": { "types": [
"maxItems_1_0": { {
"maxItems": 2, "name": "maxItems_1_0",
"extensible": true "schemas": {
"maxItems_1_0": {
"maxItems": 2,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -97,12 +107,17 @@
{ {
"description": "extensible: true allows extra items in maxItems (but counted)", "description": "extensible: true allows extra items in maxItems (but counted)",
"database": { "database": {
"schemas": { "types": [
"maxItems_2_0": { {
"maxItems": 2, "name": "maxItems_2_0",
"extensible": true "schemas": {
"maxItems_2_0": {
"maxItems": 2,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,11 +2,16 @@
{ {
"description": "maxLength validation", "description": "maxLength validation",
"database": { "database": {
"schemas": { "types": [
"maxLength_0_0": { {
"maxLength": 2 "name": "maxLength_0_0",
"schemas": {
"maxLength_0_0": {
"maxLength": 2
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -47,7 +52,7 @@
}, },
{ {
"description": "two graphemes is long enough", "description": "two graphemes is long enough",
"data": "💩💩", "data": "\ud83d\udca9\ud83d\udca9",
"schema_id": "maxLength_0_0", "schema_id": "maxLength_0_0",
"action": "validate", "action": "validate",
"expect": { "expect": {
@ -59,11 +64,16 @@
{ {
"description": "maxLength validation with a decimal", "description": "maxLength validation with a decimal",
"database": { "database": {
"schemas": { "types": [
"maxLength_1_0": { {
"maxLength": 2 "name": "maxLength_1_0",
"schemas": {
"maxLength_1_0": {
"maxLength": 2
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,12 +2,17 @@
{ {
"description": "maxProperties validation", "description": "maxProperties validation",
"database": { "database": {
"schemas": { "types": [
"maxProperties_0_0": { {
"maxProperties": 2, "name": "maxProperties_0_0",
"extensible": true "schemas": {
"maxProperties_0_0": {
"maxProperties": 2,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -82,12 +87,17 @@
{ {
"description": "maxProperties validation with a decimal", "description": "maxProperties validation with a decimal",
"database": { "database": {
"schemas": { "types": [
"maxProperties_1_0": { {
"maxProperties": 2, "name": "maxProperties_1_0",
"extensible": true "schemas": {
"maxProperties_1_0": {
"maxProperties": 2,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -119,12 +129,17 @@
{ {
"description": "maxProperties = 0 means the object is empty", "description": "maxProperties = 0 means the object is empty",
"database": { "database": {
"schemas": { "types": [
"maxProperties_2_0": { {
"maxProperties": 0, "name": "maxProperties_2_0",
"extensible": true "schemas": {
"maxProperties_2_0": {
"maxProperties": 0,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -152,12 +167,17 @@
{ {
"description": "extensible: true allows extra properties in maxProperties (though maxProperties still counts them!)", "description": "extensible: true allows extra properties in maxProperties (though maxProperties still counts them!)",
"database": { "database": {
"schemas": { "types": [
"maxProperties_3_0": { {
"maxProperties": 2, "name": "maxProperties_3_0",
"extensible": true "schemas": {
"maxProperties_3_0": {
"maxProperties": 2,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,11 +2,16 @@
{ {
"description": "maximum validation", "description": "maximum validation",
"database": { "database": {
"schemas": { "types": [
"maximum_0_0": { {
"maximum": 3 "name": "maximum_0_0",
"schemas": {
"maximum_0_0": {
"maximum": 3
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -50,11 +55,16 @@
{ {
"description": "maximum validation with unsigned integer", "description": "maximum validation with unsigned integer",
"database": { "database": {
"schemas": { "types": [
"maximum_1_0": { {
"maximum": 300 "name": "maximum_1_0",
"schemas": {
"maximum_1_0": {
"maximum": 300
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,23 +2,33 @@
{ {
"description": "merging: properties accumulate", "description": "merging: properties accumulate",
"database": { "database": {
"schemas": { "types": [
"base_0": { {
"properties": { "name": "base_0",
"base_prop": { "schemas": {
"type": "string" "base_0": {
"properties": {
"base_prop": {
"type": "string"
}
}
} }
} }
}, },
"merge_0_0": { {
"type": "base_0", "name": "merge_0_0",
"properties": { "schemas": {
"child_prop": { "merge_0_0": {
"type": "string" "type": "base_0",
"properties": {
"child_prop": {
"type": "string"
}
}
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -58,29 +68,39 @@
{ {
"description": "merging: required fields accumulate", "description": "merging: required fields accumulate",
"database": { "database": {
"schemas": { "types": [
"base_1": { {
"properties": { "name": "base_1",
"a": { "schemas": {
"type": "string" "base_1": {
"properties": {
"a": {
"type": "string"
}
},
"required": [
"a"
]
} }
}, }
"required": [
"a"
]
}, },
"merge_1_0": { {
"type": "base_1", "name": "merge_1_0",
"properties": { "schemas": {
"b": { "merge_1_0": {
"type": "string" "type": "base_1",
"properties": {
"b": {
"type": "string"
}
},
"required": [
"b"
]
} }
}, }
"required": [
"b"
]
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -138,36 +158,46 @@
{ {
"description": "merging: dependencies accumulate", "description": "merging: dependencies accumulate",
"database": { "database": {
"schemas": { "types": [
"base_2": { {
"properties": { "name": "base_2",
"trigger": { "schemas": {
"type": "string" "base_2": {
}, "properties": {
"base_dep": { "trigger": {
"type": "string" "type": "string"
},
"base_dep": {
"type": "string"
}
},
"dependencies": {
"trigger": [
"base_dep"
]
}
} }
},
"dependencies": {
"trigger": [
"base_dep"
]
} }
}, },
"merge_2_0": { {
"type": "base_2", "name": "merge_2_0",
"properties": { "schemas": {
"child_dep": { "merge_2_0": {
"type": "string" "type": "base_2",
"properties": {
"child_dep": {
"type": "string"
}
},
"dependencies": {
"trigger": [
"child_dep"
]
}
} }
},
"dependencies": {
"trigger": [
"child_dep"
]
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -228,33 +258,43 @@
{ {
"description": "merging: form and display do NOT merge", "description": "merging: form and display do NOT merge",
"database": { "database": {
"schemas": { "types": [
"base_3": { {
"properties": { "name": "base_3",
"a": { "schemas": {
"type": "string" "base_3": {
}, "properties": {
"b": { "a": {
"type": "string" "type": "string"
},
"b": {
"type": "string"
}
},
"form": [
"a",
"b"
]
} }
}, }
"form": [
"a",
"b"
]
}, },
"merge_3_0": { {
"type": "base_3", "name": "merge_3_0",
"properties": { "schemas": {
"c": { "merge_3_0": {
"type": "string" "type": "base_3",
"properties": {
"c": {
"type": "string"
}
},
"form": [
"c"
]
} }
}, }
"form": [
"c"
]
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,12 +2,17 @@
{ {
"description": "minContains without contains is ignored", "description": "minContains without contains is ignored",
"database": { "database": {
"schemas": { "types": [
"minContains_0_0": { {
"minContains": 1, "name": "minContains_0_0",
"extensible": true "schemas": {
"minContains_0_0": {
"minContains": 1,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -35,15 +40,20 @@
{ {
"description": "minContains=1 with contains", "description": "minContains=1 with contains",
"database": { "database": {
"schemas": { "types": [
"minContains_1_0": { {
"contains": { "name": "minContains_1_0",
"const": 1 "schemas": {
}, "minContains_1_0": {
"minContains": 1, "contains": {
"extensible": true "const": 1
},
"minContains": 1,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -106,15 +116,20 @@
{ {
"description": "minContains=2 with contains", "description": "minContains=2 with contains",
"database": { "database": {
"schemas": { "types": [
"minContains_2_0": { {
"contains": { "name": "minContains_2_0",
"const": 1 "schemas": {
}, "minContains_2_0": {
"minContains": 2, "contains": {
"extensible": true "const": 1
},
"minContains": 2,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -192,15 +207,20 @@
{ {
"description": "minContains=2 with contains with a decimal value", "description": "minContains=2 with contains with a decimal value",
"database": { "database": {
"schemas": { "types": [
"minContains_3_0": { {
"contains": { "name": "minContains_3_0",
"const": 1 "schemas": {
}, "minContains_3_0": {
"minContains": 2, "contains": {
"extensible": true "const": 1
},
"minContains": 2,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -231,16 +251,21 @@
{ {
"description": "maxContains = minContains", "description": "maxContains = minContains",
"database": { "database": {
"schemas": { "types": [
"minContains_4_0": { {
"contains": { "name": "minContains_4_0",
"const": 1 "schemas": {
}, "minContains_4_0": {
"maxContains": 2, "contains": {
"minContains": 2, "const": 1
"extensible": true },
"maxContains": 2,
"minContains": 2,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -293,16 +318,21 @@
{ {
"description": "maxContains < minContains", "description": "maxContains < minContains",
"database": { "database": {
"schemas": { "types": [
"minContains_5_0": { {
"contains": { "name": "minContains_5_0",
"const": 1 "schemas": {
}, "minContains_5_0": {
"maxContains": 1, "contains": {
"minContains": 3, "const": 1
"extensible": true },
"maxContains": 1,
"minContains": 3,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -355,15 +385,20 @@
{ {
"description": "minContains = 0", "description": "minContains = 0",
"database": { "database": {
"schemas": { "types": [
"minContains_6_0": { {
"contains": { "name": "minContains_6_0",
"const": 1 "schemas": {
}, "minContains_6_0": {
"minContains": 0, "contains": {
"extensible": true "const": 1
},
"minContains": 0,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -391,16 +426,21 @@
{ {
"description": "minContains = 0 with maxContains", "description": "minContains = 0 with maxContains",
"database": { "database": {
"schemas": { "types": [
"minContains_7_0": { {
"contains": { "name": "minContains_7_0",
"const": 1 "schemas": {
}, "minContains_7_0": {
"minContains": 0, "contains": {
"maxContains": 1, "const": 1
"extensible": true },
"minContains": 0,
"maxContains": 1,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -440,15 +480,20 @@
{ {
"description": "extensible: true allows non-matching items in minContains", "description": "extensible: true allows non-matching items in minContains",
"database": { "database": {
"schemas": { "types": [
"minContains_8_0": { {
"contains": { "name": "minContains_8_0",
"const": 1 "schemas": {
}, "minContains_8_0": {
"minContains": 1, "contains": {
"extensible": true "const": 1
},
"minContains": 1,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,12 +2,17 @@
{ {
"description": "minItems validation", "description": "minItems validation",
"database": { "database": {
"schemas": { "types": [
"minItems_0_0": { {
"minItems": 1, "name": "minItems_0_0",
"extensible": true "schemas": {
"minItems_0_0": {
"minItems": 1,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -56,12 +61,17 @@
{ {
"description": "minItems validation with a decimal", "description": "minItems validation with a decimal",
"database": { "database": {
"schemas": { "types": [
"minItems_1_0": { {
"minItems": 1, "name": "minItems_1_0",
"extensible": true "schemas": {
"minItems_1_0": {
"minItems": 1,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -90,12 +100,17 @@
{ {
"description": "extensible: true allows extra items in minItems", "description": "extensible: true allows extra items in minItems",
"database": { "database": {
"schemas": { "types": [
"minItems_2_0": { {
"minItems": 1, "name": "minItems_2_0",
"extensible": true "schemas": {
"minItems_2_0": {
"minItems": 1,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,11 +2,16 @@
{ {
"description": "minLength validation", "description": "minLength validation",
"database": { "database": {
"schemas": { "types": [
"minLength_0_0": { {
"minLength": 2 "name": "minLength_0_0",
"schemas": {
"minLength_0_0": {
"minLength": 2
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -47,7 +52,7 @@
}, },
{ {
"description": "one grapheme is not long enough", "description": "one grapheme is not long enough",
"data": "💩", "data": "\ud83d\udca9",
"schema_id": "minLength_0_0", "schema_id": "minLength_0_0",
"action": "validate", "action": "validate",
"expect": { "expect": {
@ -59,11 +64,16 @@
{ {
"description": "minLength validation with a decimal", "description": "minLength validation with a decimal",
"database": { "database": {
"schemas": { "types": [
"minLength_1_0": { {
"minLength": 2 "name": "minLength_1_0",
"schemas": {
"minLength_1_0": {
"minLength": 2
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,12 +2,17 @@
{ {
"description": "minProperties validation", "description": "minProperties validation",
"database": { "database": {
"schemas": { "types": [
"minProperties_0_0": { {
"minProperties": 1, "name": "minProperties_0_0",
"extensible": true "schemas": {
"minProperties_0_0": {
"minProperties": 1,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -74,12 +79,17 @@
{ {
"description": "minProperties validation with a decimal", "description": "minProperties validation with a decimal",
"database": { "database": {
"schemas": { "types": [
"minProperties_1_0": { {
"minProperties": 1, "name": "minProperties_1_0",
"extensible": true "schemas": {
"minProperties_1_0": {
"minProperties": 1,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -108,12 +118,17 @@
{ {
"description": "extensible: true allows extra properties in minProperties", "description": "extensible: true allows extra properties in minProperties",
"database": { "database": {
"schemas": { "types": [
"minProperties_2_0": { {
"minProperties": 1, "name": "minProperties_2_0",
"extensible": true "schemas": {
"minProperties_2_0": {
"minProperties": 1,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,11 +2,16 @@
{ {
"description": "minimum validation", "description": "minimum validation",
"database": { "database": {
"schemas": { "types": [
"minimum_0_0": { {
"minimum": 1.1 "name": "minimum_0_0",
"schemas": {
"minimum_0_0": {
"minimum": 1.1
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -50,11 +55,16 @@
{ {
"description": "minimum validation with signed integer", "description": "minimum validation with signed integer",
"database": { "database": {
"schemas": { "types": [
"minimum_1_0": { {
"minimum": -2 "name": "minimum_1_0",
"schemas": {
"minimum_1_0": {
"minimum": -2
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,11 +2,16 @@
{ {
"description": "by int", "description": "by int",
"database": { "database": {
"schemas": { "types": [
"multipleOf_0_0": { {
"multipleOf": 2 "name": "multipleOf_0_0",
"schemas": {
"multipleOf_0_0": {
"multipleOf": 2
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -41,11 +46,16 @@
{ {
"description": "by number", "description": "by number",
"database": { "database": {
"schemas": { "types": [
"multipleOf_1_0": { {
"multipleOf": 1.5 "name": "multipleOf_1_0",
"schemas": {
"multipleOf_1_0": {
"multipleOf": 1.5
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -80,11 +90,16 @@
{ {
"description": "by small number", "description": "by small number",
"database": { "database": {
"schemas": { "types": [
"multipleOf_2_0": { {
"multipleOf": 0.0001 "name": "multipleOf_2_0",
"schemas": {
"multipleOf_2_0": {
"multipleOf": 0.0001
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -110,12 +125,17 @@
{ {
"description": "small multiple of large integer", "description": "small multiple of large integer",
"database": { "database": {
"schemas": { "types": [
"multipleOf_3_0": { {
"type": "integer", "name": "multipleOf_3_0",
"multipleOf": 1e-08 "schemas": {
"multipleOf_3_0": {
"type": "integer",
"multipleOf": 1e-08
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,13 +2,18 @@
{ {
"description": "not", "description": "not",
"database": { "database": {
"schemas": { "types": [
"not_0_0": { {
"not": { "name": "not_0_0",
"type": "integer" "schemas": {
"not_0_0": {
"not": {
"type": "integer"
}
}
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -34,16 +39,21 @@
{ {
"description": "not multiple types", "description": "not multiple types",
"database": { "database": {
"schemas": { "types": [
"not_1_0": { {
"not": { "name": "not_1_0",
"type": [ "schemas": {
"integer", "not_1_0": {
"boolean" "not": {
] "type": [
"integer",
"boolean"
]
}
}
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -78,19 +88,24 @@
{ {
"description": "not more complex schema", "description": "not more complex schema",
"database": { "database": {
"schemas": { "types": [
"not_2_0": { {
"not": { "name": "not_2_0",
"type": "object", "schemas": {
"properties": { "not_2_0": {
"foo": { "not": {
"type": "string" "type": "object",
} "properties": {
"foo": {
"type": "string"
}
}
},
"extensible": true
} }
}, }
"extensible": true
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -129,15 +144,20 @@
{ {
"description": "forbidden property", "description": "forbidden property",
"database": { "database": {
"schemas": { "types": [
"not_3_0": { {
"properties": { "name": "not_3_0",
"foo": { "schemas": {
"not": {} "not_3_0": {
"properties": {
"foo": {
"not": {}
}
}
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -166,11 +186,16 @@
{ {
"description": "forbid everything with empty schema", "description": "forbid everything with empty schema",
"database": { "database": {
"schemas": { "types": [
"not_4_0": { {
"not": {} "name": "not_4_0",
"schemas": {
"not_4_0": {
"not": {}
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -263,11 +288,16 @@
{ {
"description": "forbid everything with boolean schema true", "description": "forbid everything with boolean schema true",
"database": { "database": {
"schemas": { "types": [
"not_5_0": { {
"not": true "name": "not_5_0",
"schemas": {
"not_5_0": {
"not": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -360,12 +390,17 @@
{ {
"description": "allow everything with boolean schema false", "description": "allow everything with boolean schema false",
"database": { "database": {
"schemas": { "types": [
"not_6_0": { {
"not": false, "name": "not_6_0",
"extensible": true "schemas": {
"not_6_0": {
"not": false,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -458,13 +493,18 @@
{ {
"description": "double negation", "description": "double negation",
"database": { "database": {
"schemas": { "types": [
"not_7_0": { {
"not": { "name": "not_7_0",
"not": {} "schemas": {
"not_7_0": {
"not": {
"not": {}
}
}
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -481,14 +521,19 @@
{ {
"description": "extensible: true allows extra properties in not", "description": "extensible: true allows extra properties in not",
"database": { "database": {
"schemas": { "types": [
"not_8_0": { {
"not": { "name": "not_8_0",
"type": "integer" "schemas": {
}, "not_8_0": {
"extensible": true "not": {
"type": "integer"
},
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -507,13 +552,18 @@
{ {
"description": "extensible: false (default) forbids extra properties in not", "description": "extensible: false (default) forbids extra properties in not",
"database": { "database": {
"schemas": { "types": [
"not_9_0": { {
"not": { "name": "not_9_0",
"type": "integer" "schemas": {
"not_9_0": {
"not": {
"type": "integer"
}
}
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -532,19 +582,24 @@
{ {
"description": "property next to not (extensible: true)", "description": "property next to not (extensible: true)",
"database": { "database": {
"schemas": { "types": [
"not_10_0": { {
"properties": { "name": "not_10_0",
"bar": { "schemas": {
"type": "string" "not_10_0": {
"properties": {
"bar": {
"type": "string"
}
},
"not": {
"type": "integer"
},
"extensible": true
} }
}, }
"not": {
"type": "integer"
},
"extensible": true
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -564,18 +619,23 @@
{ {
"description": "property next to not (extensible: false)", "description": "property next to not (extensible: false)",
"database": { "database": {
"schemas": { "types": [
"not_11_0": { {
"properties": { "name": "not_11_0",
"bar": { "schemas": {
"type": "string" "not_11_0": {
"properties": {
"bar": {
"type": "string"
}
},
"not": {
"type": "integer"
}
} }
},
"not": {
"type": "integer"
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,27 +2,37 @@
{ {
"description": "Strict Inheritance", "description": "Strict Inheritance",
"database": { "database": {
"schemas": { "types": [
"parent_type": { {
"type": "object", "name": "parent_type",
"properties": { "schemas": {
"a": { "parent_type": {
"type": "integer" "type": "object",
"properties": {
"a": {
"type": "integer"
}
},
"required": [
"a"
]
} }
}, }
"required": [
"a"
]
}, },
"child_type": { {
"type": "parent_type", "name": "child_type",
"properties": { "schemas": {
"b": { "child_type": {
"type": "integer" "type": "parent_type",
"properties": {
"b": {
"type": "integer"
}
}
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -66,26 +76,36 @@
{ {
"description": "Local Shadowing (Composition & Proxies)", "description": "Local Shadowing (Composition & Proxies)",
"database": { "database": {
"schemas": { "types": [
"budget": { {
"type": "object", "name": "budget",
"properties": { "schemas": {
"max": { "budget": {
"type": "integer", "type": "object",
"maximum": 100 "properties": {
"max": {
"type": "integer",
"maximum": 100
}
}
} }
} }
}, },
"custom_budget": { {
"type": "budget", "name": "custom_budget",
"properties": { "schemas": {
"max": { "custom_budget": {
"type": "integer", "type": "budget",
"maximum": 50 "properties": {
"max": {
"type": "integer",
"maximum": 50
}
}
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -115,30 +135,40 @@
{ {
"description": "Primitive Array Shorthand (Optionality)", "description": "Primitive Array Shorthand (Optionality)",
"database": { "database": {
"schemas": { "types": [
"invoice": { {
"type": "object", "name": "invoice",
"properties": { "schemas": {
"amount": { "invoice": {
"type": "integer" "type": "object",
} "properties": {
}, "amount": {
"required": [ "type": "integer"
"amount" }
] },
}, "required": [
"request": { "amount"
"type": "object",
"properties": {
"inv": {
"type": [
"invoice",
"null"
] ]
} }
} }
},
{
"name": "request",
"schemas": {
"request": {
"type": "object",
"properties": {
"inv": {
"type": [
"invoice",
"null"
]
}
}
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,63 +2,68 @@
{ {
"description": "Hybrid Array Pathing", "description": "Hybrid Array Pathing",
"database": { "database": {
"schemas": { "types": [
"hybrid_pathing": { {
"type": "object", "name": "hybrid_pathing",
"properties": { "schemas": {
"primitives": { "hybrid_pathing": {
"type": "array", "type": "object",
"items": { "properties": {
"type": "string" "primitives": {
} "type": "array",
}, "items": {
"ad_hoc_objects": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string" "type": "string"
} }
}, },
"required": [ "ad_hoc_objects": {
"name" "type": "array",
] "items": {
} "type": "object",
}, "properties": {
"entities": { "name": {
"type": "array", "type": "string"
"items": { }
"type": "object", },
"properties": { "required": [
"id": { "name"
"type": "string" ]
},
"value": {
"type": "number",
"minimum": 10
} }
} },
} "entities": {
}, "type": "array",
"deep_entities": { "items": {
"type": "array", "type": "object",
"items": { "properties": {
"type": "object", "id": {
"properties": { "type": "string"
"id": { },
"type": "string" "value": {
}, "type": "number",
"nested": { "minimum": 10
"type": "array", }
"items": { }
"type": "object", }
"properties": { },
"id": { "deep_entities": {
"type": "string" "type": "array",
}, "items": {
"flag": { "type": "object",
"type": "boolean" "properties": {
"id": {
"type": "string"
},
"nested": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"flag": {
"type": "boolean"
}
}
} }
} }
} }
@ -68,7 +73,7 @@
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -227,26 +232,31 @@
{ {
"description": "Ad-Hoc Union Pathing", "description": "Ad-Hoc Union Pathing",
"database": { "database": {
"schemas": { "types": [
"ad_hoc_pathing": { {
"type": "object", "name": "ad_hoc_pathing",
"properties": { "schemas": {
"metadata_bubbles": { "ad_hoc_pathing": {
"type": "array", "type": "object",
"items": { "properties": {
"oneOf": [ "metadata_bubbles": {
{ "type": "array",
"type": "string" "items": {
}, "oneOf": [
{ {
"type": "integer" "type": "string"
},
{
"type": "integer"
}
]
} }
] }
} }
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -277,9 +287,22 @@
{ {
"description": "Polymorphic Family Pathing", "description": "Polymorphic Family Pathing",
"database": { "database": {
"relations": [
{
"id": "11111111-1111-1111-1111-111111111111",
"type": "relation",
"constraint": "fk_family_pathing_table_families_widget",
"source_type": "widget",
"source_columns": ["family_pathing_id"],
"destination_type": "family_pathing",
"destination_columns": ["id"],
"prefix": "table_families"
}
],
"types": [ "types": [
{ {
"name": "widget", "name": "widget",
"hierarchy": ["widget"],
"variations": [ "variations": [
"widget" "widget"
], ],
@ -322,21 +345,25 @@
] ]
} }
} }
} },
], {
"schemas": { "name": "family_pathing",
"family_pathing": { "hierarchy": ["family_pathing"],
"type": "object", "schemas": {
"properties": { "family_pathing": {
"table_families": { "type": "object",
"type": "array", "properties": {
"items": { "table_families": {
"family": "widget" "type": "array",
"items": {
"family": "widget"
}
}
} }
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,11 +2,16 @@
{ {
"description": "pattern validation", "description": "pattern validation",
"database": { "database": {
"schemas": { "types": [
"pattern_0_0": { {
"pattern": "^a*$" "name": "pattern_0_0",
"schemas": {
"pattern_0_0": {
"pattern": "^a*$"
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -86,11 +91,16 @@
{ {
"description": "pattern is not anchored", "description": "pattern is not anchored",
"database": { "database": {
"schemas": { "types": [
"pattern_1_0": { {
"pattern": "a+" "name": "pattern_1_0",
"schemas": {
"pattern_1_0": {
"pattern": "a+"
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,16 +2,21 @@
{ {
"description": "patternProperties validates properties matching a regex", "description": "patternProperties validates properties matching a regex",
"database": { "database": {
"schemas": { "types": [
"patternProperties_0_0": { {
"patternProperties": { "name": "patternProperties_0_0",
"f.*o": { "schemas": {
"type": "integer" "patternProperties_0_0": {
"patternProperties": {
"f.*o": {
"type": "integer"
}
},
"items": {}
} }
}, }
"items": {}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -107,18 +112,23 @@
{ {
"description": "multiple simultaneous patternProperties are validated", "description": "multiple simultaneous patternProperties are validated",
"database": { "database": {
"schemas": { "types": [
"patternProperties_1_0": { {
"patternProperties": { "name": "patternProperties_1_0",
"a*": { "schemas": {
"type": "integer" "patternProperties_1_0": {
}, "patternProperties": {
"aaa*": { "a*": {
"maximum": 20 "type": "integer"
},
"aaa*": {
"maximum": 20
}
}
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -194,19 +204,24 @@
{ {
"description": "regexes are not anchored by default and are case sensitive", "description": "regexes are not anchored by default and are case sensitive",
"database": { "database": {
"schemas": { "types": [
"patternProperties_2_0": { {
"patternProperties": { "name": "patternProperties_2_0",
"[0-9]{2,}": { "schemas": {
"type": "boolean" "patternProperties_2_0": {
}, "patternProperties": {
"X_": { "[0-9]{2,}": {
"type": "string" "type": "boolean"
},
"X_": {
"type": "string"
}
},
"extensible": true
} }
}, }
"extensible": true
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -258,14 +273,19 @@
{ {
"description": "patternProperties with boolean schemas", "description": "patternProperties with boolean schemas",
"database": { "database": {
"schemas": { "types": [
"patternProperties_3_0": { {
"patternProperties": { "name": "patternProperties_3_0",
"f.*": true, "schemas": {
"b.*": false "patternProperties_3_0": {
"patternProperties": {
"f.*": true,
"b.*": false
}
}
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -327,15 +347,20 @@
{ {
"description": "patternProperties with null valued instance properties", "description": "patternProperties with null valued instance properties",
"database": { "database": {
"schemas": { "types": [
"patternProperties_4_0": { {
"patternProperties": { "name": "patternProperties_4_0",
"^.*bar$": { "schemas": {
"type": "null" "patternProperties_4_0": {
"patternProperties": {
"^.*bar$": {
"type": "null"
}
}
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -354,16 +379,21 @@
{ {
"description": "extensible: true allows extra properties NOT matching pattern", "description": "extensible: true allows extra properties NOT matching pattern",
"database": { "database": {
"schemas": { "types": [
"patternProperties_5_0": { {
"patternProperties": { "name": "patternProperties_5_0",
"f.*o": { "schemas": {
"type": "integer" "patternProperties_5_0": {
"patternProperties": {
"f.*o": {
"type": "integer"
}
},
"extensible": true
} }
}, }
"extensible": true
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -73,13 +73,16 @@
} }
} }
} }
},
{
"name": "family_entity",
"schemas": {
"family_entity": {
"family": "entity"
}
}
} }
], ]
"schemas": {
"family_entity": {
"family": "entity"
}
}
}, },
"tests": [ "tests": [
{ {
@ -222,13 +225,16 @@
"type": "light.entity" "type": "light.entity"
} }
} }
},
{
"name": "family_light_org",
"schemas": {
"family_light_org": {
"family": "light.organization"
}
}
} }
], ]
"schemas": {
"family_light_org": {
"family": "light.organization"
}
}
}, },
"tests": [ "tests": [
{ {
@ -315,16 +321,24 @@
} }
} }
} }
}
],
"schemas": {
"family_widget": {
"family": "widget"
}, },
"family_stock_widget": { {
"family": "stock.widget" "name": "family_widget",
"schemas": {
"family_widget": {
"family": "widget"
}
}
},
{
"name": "family_stock_widget",
"schemas": {
"family_stock_widget": {
"family": "stock.widget"
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -463,20 +477,23 @@
] ]
} }
} }
} },
], {
"schemas": { "name": "oneOf_union",
"oneOf_union": { "schemas": {
"oneOf": [ "oneOf_union": {
{ "oneOf": [
"type": "full.person" {
}, "type": "full.person"
{ },
"type": "full.bot" {
"type": "full.bot"
}
]
} }
] }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -559,48 +576,58 @@
{ {
"description": "JSONB Field Bubble oneOf Discrimination (Promoted IDs)", "description": "JSONB Field Bubble oneOf Discrimination (Promoted IDs)",
"database": { "database": {
"schemas": { "types": [
"metadata": { {
"type": "object", "name": "metadata",
"properties": { "schemas": {
"type": { "metadata": {
"type": "string" "type": "object",
"properties": {
"type": {
"type": "string"
}
}
},
"invoice.metadata": {
"type": "metadata",
"properties": {
"invoice_id": {
"type": "integer"
}
},
"required": [
"invoice_id"
]
},
"payment.metadata": {
"type": "metadata",
"properties": {
"payment_id": {
"type": "integer"
}
},
"required": [
"payment_id"
]
} }
} }
}, },
"invoice.metadata": { {
"type": "metadata", "name": "oneOf_bubble",
"properties": { "schemas": {
"invoice_id": { "oneOf_bubble": {
"type": "integer" "oneOf": [
{
"type": "invoice.metadata"
},
{
"type": "payment.metadata"
}
]
} }
}, }
"required": [
"invoice_id"
]
},
"payment.metadata": {
"type": "metadata",
"properties": {
"payment_id": {
"type": "integer"
}
},
"required": [
"payment_id"
]
},
"oneOf_bubble": {
"oneOf": [
{
"type": "invoice.metadata"
},
{
"type": "payment.metadata"
}
]
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -675,16 +702,24 @@
} }
} }
} }
}
],
"schemas": {
"stock_widget_validation": {
"type": "stock.widget"
}, },
"projected_widget_validation": { {
"type": "projected.widget" "name": "stock_widget_validation",
"schemas": {
"stock_widget_validation": {
"type": "stock.widget"
}
}
},
{
"name": "projected_widget_validation",
"schemas": {
"projected_widget_validation": {
"type": "projected.widget"
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,18 +2,23 @@
{ {
"description": "a schema given for prefixItems", "description": "a schema given for prefixItems",
"database": { "database": {
"schemas": { "types": [
"prefixItems_0_0": { {
"prefixItems": [ "name": "prefixItems_0_0",
{ "schemas": {
"type": "integer" "prefixItems_0_0": {
}, "prefixItems": [
{ {
"type": "string" "type": "integer"
},
{
"type": "string"
}
]
} }
] }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -91,14 +96,19 @@
{ {
"description": "prefixItems with boolean schemas", "description": "prefixItems with boolean schemas",
"database": { "database": {
"schemas": { "types": [
"prefixItems_1_0": { {
"prefixItems": [ "name": "prefixItems_1_0",
true, "schemas": {
false "prefixItems_1_0": {
] "prefixItems": [
true,
false
]
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -138,16 +148,21 @@
{ {
"description": "additional items are allowed by default", "description": "additional items are allowed by default",
"database": { "database": {
"schemas": { "types": [
"prefixItems_2_0": { {
"prefixItems": [ "name": "prefixItems_2_0",
{ "schemas": {
"type": "integer" "prefixItems_2_0": {
"prefixItems": [
{
"type": "integer"
}
],
"extensible": true
} }
], }
"extensible": true
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -168,15 +183,20 @@
{ {
"description": "prefixItems with null instance elements", "description": "prefixItems with null instance elements",
"database": { "database": {
"schemas": { "types": [
"prefixItems_3_0": { {
"prefixItems": [ "name": "prefixItems_3_0",
{ "schemas": {
"type": "null" "prefixItems_3_0": {
"prefixItems": [
{
"type": "null"
}
]
} }
] }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -195,16 +215,21 @@
{ {
"description": "extensible: true allows extra items with prefixItems", "description": "extensible: true allows extra items with prefixItems",
"database": { "database": {
"schemas": { "types": [
"prefixItems_4_0": { {
"prefixItems": [ "name": "prefixItems_4_0",
{ "schemas": {
"type": "integer" "prefixItems_4_0": {
"prefixItems": [
{
"type": "integer"
}
],
"extensible": true
} }
], }
"extensible": true
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,11 +2,16 @@
{ {
"description": "integer type matches integers", "description": "integer type matches integers",
"database": { "database": {
"schemas": { "types": [
"type_0_0": { {
"type": "integer" "name": "type_0_0",
"schemas": {
"type_0_0": {
"type": "integer"
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -95,11 +100,16 @@
{ {
"description": "number type matches numbers", "description": "number type matches numbers",
"database": { "database": {
"schemas": { "types": [
"type_1_0": { {
"type": "number" "name": "type_1_0",
"schemas": {
"type_1_0": {
"type": "number"
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -188,11 +198,16 @@
{ {
"description": "string type matches strings", "description": "string type matches strings",
"database": { "database": {
"schemas": { "types": [
"type_2_0": { {
"type": "string" "name": "type_2_0",
"schemas": {
"type_2_0": {
"type": "string"
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -281,11 +296,16 @@
{ {
"description": "object type matches objects", "description": "object type matches objects",
"database": { "database": {
"schemas": { "types": [
"type_3_0": { {
"type": "object" "name": "type_3_0",
"schemas": {
"type_3_0": {
"type": "object"
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -356,11 +376,16 @@
{ {
"description": "array type matches arrays", "description": "array type matches arrays",
"database": { "database": {
"schemas": { "types": [
"type_4_0": { {
"type": "array" "name": "type_4_0",
"schemas": {
"type_4_0": {
"type": "array"
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -431,11 +456,16 @@
{ {
"description": "boolean type matches booleans", "description": "boolean type matches booleans",
"database": { "database": {
"schemas": { "types": [
"type_5_0": { {
"type": "boolean" "name": "type_5_0",
"schemas": {
"type_5_0": {
"type": "boolean"
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -533,11 +563,16 @@
{ {
"description": "null type matches only the null object", "description": "null type matches only the null object",
"database": { "database": {
"schemas": { "types": [
"type_6_0": { {
"type": "null" "name": "type_6_0",
"schemas": {
"type_6_0": {
"type": "null"
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -635,14 +670,19 @@
{ {
"description": "multiple types can be specified in an array", "description": "multiple types can be specified in an array",
"database": { "database": {
"schemas": { "types": [
"type_7_0": { {
"type": [ "name": "type_7_0",
"integer", "schemas": {
"string" "type_7_0": {
] "type": [
"integer",
"string"
]
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -713,13 +753,18 @@
{ {
"description": "type as array with one item", "description": "type as array with one item",
"database": { "database": {
"schemas": { "types": [
"type_8_0": { {
"type": [ "name": "type_8_0",
"string" "schemas": {
] "type_8_0": {
"type": [
"string"
]
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -745,15 +790,20 @@
{ {
"description": "type: array or object", "description": "type: array or object",
"database": { "database": {
"schemas": { "types": [
"type_9_0": { {
"type": [ "name": "type_9_0",
"array", "schemas": {
"object" "type_9_0": {
], "type": [
"items": {} "array",
"object"
],
"items": {}
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -810,16 +860,21 @@
{ {
"description": "type: array, object or null", "description": "type: array, object or null",
"database": { "database": {
"schemas": { "types": [
"type_10_0": { {
"type": [ "name": "type_10_0",
"array", "schemas": {
"object", "type_10_0": {
"null" "type": [
], "array",
"items": {} "object",
"null"
],
"items": {}
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -876,12 +931,17 @@
{ {
"description": "extensible: true allows extra properties", "description": "extensible: true allows extra properties",
"database": { "database": {
"schemas": { "types": [
"type_11_0": { {
"type": "object", "name": "type_11_0",
"extensible": true "schemas": {
"type_11_0": {
"type": "object",
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,18 +2,23 @@
{ {
"description": "object properties validation", "description": "object properties validation",
"database": { "database": {
"schemas": { "types": [
"properties_0_0": { {
"properties": { "name": "properties_0_0",
"foo": { "schemas": {
"type": "integer" "properties_0_0": {
}, "properties": {
"bar": { "foo": {
"type": "string" "type": "integer"
},
"bar": {
"type": "string"
}
}
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -84,14 +89,19 @@
{ {
"description": "properties with boolean schema", "description": "properties with boolean schema",
"database": { "database": {
"schemas": { "types": [
"properties_1_0": { {
"properties": { "name": "properties_1_0",
"foo": true, "schemas": {
"bar": false "properties_1_0": {
"properties": {
"foo": true,
"bar": false
}
}
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -142,30 +152,35 @@
{ {
"description": "properties with escaped characters", "description": "properties with escaped characters",
"database": { "database": {
"schemas": { "types": [
"properties_2_0": { {
"properties": { "name": "properties_2_0",
"foo\nbar": { "schemas": {
"type": "number" "properties_2_0": {
}, "properties": {
"foo\"bar": { "foo\nbar": {
"type": "number" "type": "number"
}, },
"foo\\bar": { "foo\"bar": {
"type": "number" "type": "number"
}, },
"foo\rbar": { "foo\\bar": {
"type": "number" "type": "number"
}, },
"foo\tbar": { "foo\rbar": {
"type": "number" "type": "number"
}, },
"foo\fbar": { "foo\tbar": {
"type": "number" "type": "number"
},
"foo\fbar": {
"type": "number"
}
}
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -205,15 +220,20 @@
{ {
"description": "properties with null valued instance properties", "description": "properties with null valued instance properties",
"database": { "database": {
"schemas": { "types": [
"properties_3_0": { {
"properties": { "name": "properties_3_0",
"foo": { "schemas": {
"type": "null" "properties_3_0": {
"properties": {
"foo": {
"type": "null"
}
}
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -233,25 +253,30 @@
"description": "properties whose names are Javascript object property names", "description": "properties whose names are Javascript object property names",
"comment": "Ensure JS implementations don't universally consider e.g. __proto__ to always be present in an object.", "comment": "Ensure JS implementations don't universally consider e.g. __proto__ to always be present in an object.",
"database": { "database": {
"schemas": { "types": [
"properties_4_0": { {
"properties": { "name": "properties_4_0",
"__proto__": { "schemas": {
"type": "number" "properties_4_0": {
},
"toString": {
"properties": { "properties": {
"length": { "__proto__": {
"type": "string" "type": "number"
},
"toString": {
"properties": {
"length": {
"type": "string"
}
}
},
"constructor": {
"type": "number"
} }
} }
},
"constructor": {
"type": "number"
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -338,16 +363,21 @@
{ {
"description": "extensible: true allows extra properties", "description": "extensible: true allows extra properties",
"database": { "database": {
"schemas": { "types": [
"properties_5_0": { {
"properties": { "name": "properties_5_0",
"foo": { "schemas": {
"type": "integer" "properties_5_0": {
"properties": {
"foo": {
"type": "integer"
}
},
"extensible": true
} }
}, }
"extensible": true
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -367,15 +397,20 @@
{ {
"description": "strict by default: extra properties invalid", "description": "strict by default: extra properties invalid",
"database": { "database": {
"schemas": { "types": [
"properties_6_0": { {
"properties": { "name": "properties_6_0",
"foo": { "schemas": {
"type": "string" "properties_6_0": {
"properties": {
"foo": {
"type": "string"
}
}
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -395,19 +430,24 @@
{ {
"description": "inheritance: nested object inherits strictness from strict parent", "description": "inheritance: nested object inherits strictness from strict parent",
"database": { "database": {
"schemas": { "types": [
"properties_7_0": { {
"properties": { "name": "properties_7_0",
"nested": { "schemas": {
"properties_7_0": {
"properties": { "properties": {
"foo": { "nested": {
"type": "string" "properties": {
"foo": {
"type": "string"
}
}
} }
} }
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -429,20 +469,25 @@
{ {
"description": "override: nested object allows extra properties if extensible: true", "description": "override: nested object allows extra properties if extensible: true",
"database": { "database": {
"schemas": { "types": [
"properties_8_0": { {
"properties": { "name": "properties_8_0",
"nested": { "schemas": {
"extensible": true, "properties_8_0": {
"properties": { "properties": {
"foo": { "nested": {
"type": "string" "extensible": true,
"properties": {
"foo": {
"type": "string"
}
}
} }
} }
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -464,20 +509,25 @@
{ {
"description": "inheritance: nested object inherits looseness from loose parent", "description": "inheritance: nested object inherits looseness from loose parent",
"database": { "database": {
"schemas": { "types": [
"properties_9_0": { {
"extensible": true, "name": "properties_9_0",
"properties": { "schemas": {
"nested": { "properties_9_0": {
"extensible": true,
"properties": { "properties": {
"foo": { "nested": {
"type": "string" "properties": {
"foo": {
"type": "string"
}
}
} }
} }
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -499,21 +549,26 @@
{ {
"description": "override: nested object enforces strictness if extensible: false", "description": "override: nested object enforces strictness if extensible: false",
"database": { "database": {
"schemas": { "types": [
"properties_10_0": { {
"extensible": true, "name": "properties_10_0",
"properties": { "schemas": {
"nested": { "properties_10_0": {
"extensible": false, "extensible": true,
"properties": { "properties": {
"foo": { "nested": {
"type": "string" "extensible": false,
"properties": {
"foo": {
"type": "string"
}
}
} }
} }
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -535,22 +590,27 @@
{ {
"description": "arrays: inline items inherit strictness from strict parent", "description": "arrays: inline items inherit strictness from strict parent",
"database": { "database": {
"schemas": { "types": [
"properties_11_0": { {
"properties": { "name": "properties_11_0",
"list": { "schemas": {
"type": "array", "properties_11_0": {
"items": { "properties": {
"properties": { "list": {
"foo": { "type": "array",
"type": "string" "items": {
"properties": {
"foo": {
"type": "string"
}
}
} }
} }
} }
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -574,23 +634,28 @@
{ {
"description": "arrays: inline items inherit looseness from loose parent", "description": "arrays: inline items inherit looseness from loose parent",
"database": { "database": {
"schemas": { "types": [
"properties_12_0": { {
"extensible": true, "name": "properties_12_0",
"properties": { "schemas": {
"list": { "properties_12_0": {
"type": "array", "extensible": true,
"items": { "properties": {
"properties": { "list": {
"foo": { "type": "array",
"type": "string" "items": {
"properties": {
"foo": {
"type": "string"
}
}
} }
} }
} }
} }
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,14 +2,19 @@
{ {
"description": "propertyNames validation", "description": "propertyNames validation",
"database": { "database": {
"schemas": { "types": [
"propertyNames_0_0": { {
"propertyNames": { "name": "propertyNames_0_0",
"maxLength": 3 "schemas": {
}, "propertyNames_0_0": {
"extensible": true "propertyNames": {
"maxLength": 3
},
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -82,14 +87,19 @@
{ {
"description": "propertyNames validation with pattern", "description": "propertyNames validation with pattern",
"database": { "database": {
"schemas": { "types": [
"propertyNames_1_0": { {
"propertyNames": { "name": "propertyNames_1_0",
"pattern": "^a+$" "schemas": {
}, "propertyNames_1_0": {
"extensible": true "propertyNames": {
"pattern": "^a+$"
},
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -130,12 +140,17 @@
{ {
"description": "propertyNames with boolean schema true", "description": "propertyNames with boolean schema true",
"database": { "database": {
"schemas": { "types": [
"propertyNames_2_0": { {
"propertyNames": true, "name": "propertyNames_2_0",
"extensible": true "schemas": {
"propertyNames_2_0": {
"propertyNames": true,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -163,12 +178,17 @@
{ {
"description": "propertyNames with boolean schema false", "description": "propertyNames with boolean schema false",
"database": { "database": {
"schemas": { "types": [
"propertyNames_3_0": { {
"propertyNames": false, "name": "propertyNames_3_0",
"extensible": true "schemas": {
"propertyNames_3_0": {
"propertyNames": false,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -196,14 +216,19 @@
{ {
"description": "propertyNames with const", "description": "propertyNames with const",
"database": { "database": {
"schemas": { "types": [
"propertyNames_4_0": { {
"propertyNames": { "name": "propertyNames_4_0",
"const": "foo" "schemas": {
}, "propertyNames_4_0": {
"extensible": true "propertyNames": {
"const": "foo"
},
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -242,17 +267,22 @@
{ {
"description": "propertyNames with enum", "description": "propertyNames with enum",
"database": { "database": {
"schemas": { "types": [
"propertyNames_5_0": { {
"propertyNames": { "name": "propertyNames_5_0",
"enum": [ "schemas": {
"foo", "propertyNames_5_0": {
"bar" "propertyNames": {
] "enum": [
}, "foo",
"extensible": true "bar"
]
},
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -303,14 +333,19 @@
{ {
"description": "extensible: true allows extra properties (checked by propertyNames)", "description": "extensible: true allows extra properties (checked by propertyNames)",
"database": { "database": {
"schemas": { "types": [
"propertyNames_6_0": { {
"propertyNames": { "name": "propertyNames_6_0",
"maxLength": 3 "schemas": {
}, "propertyNames_6_0": {
"extensible": true "propertyNames": {
"maxLength": 3
},
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,17 +2,22 @@
{ {
"description": "required validation", "description": "required validation",
"database": { "database": {
"schemas": { "types": [
"required_0_0": { {
"properties": { "name": "required_0_0",
"foo": {}, "schemas": {
"bar": {} "required_0_0": {
}, "properties": {
"required": [ "foo": {},
"foo" "bar": {}
] },
"required": [
"foo"
]
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -87,13 +92,18 @@
{ {
"description": "required default validation", "description": "required default validation",
"database": { "database": {
"schemas": { "types": [
"required_1_0": { {
"properties": { "name": "required_1_0",
"foo": {} "schemas": {
"required_1_0": {
"properties": {
"foo": {}
}
}
} }
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -110,14 +120,19 @@
{ {
"description": "required with empty array", "description": "required with empty array",
"database": { "database": {
"schemas": { "types": [
"required_2_0": { {
"properties": { "name": "required_2_0",
"foo": {} "schemas": {
}, "required_2_0": {
"required": [] "properties": {
"foo": {}
},
"required": []
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -134,19 +149,24 @@
{ {
"description": "required with escaped characters", "description": "required with escaped characters",
"database": { "database": {
"schemas": { "types": [
"required_3_0": { {
"required": [ "name": "required_3_0",
"foo\nbar", "schemas": {
"foo\"bar", "required_3_0": {
"foo\\bar", "required": [
"foo\rbar", "foo\nbar",
"foo\tbar", "foo\"bar",
"foo\fbar" "foo\\bar",
], "foo\rbar",
"extensible": true "foo\tbar",
"foo\fbar"
],
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -183,16 +203,21 @@
"description": "required properties whose names are Javascript object property names", "description": "required properties whose names are Javascript object property names",
"comment": "Ensure JS implementations don't universally consider e.g. __proto__ to always be present in an object.", "comment": "Ensure JS implementations don't universally consider e.g. __proto__ to always be present in an object.",
"database": { "database": {
"schemas": { "types": [
"required_4_0": { {
"required": [ "name": "required_4_0",
"__proto__", "schemas": {
"toString", "required_4_0": {
"constructor" "required": [
], "__proto__",
"extensible": true "toString",
"constructor"
],
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -279,14 +304,19 @@
{ {
"description": "extensible: true allows extra properties in required", "description": "extensible: true allows extra properties in required",
"database": { "database": {
"schemas": { "types": [
"required_5_0": { {
"required": [ "name": "required_5_0",
"foo" "schemas": {
], "required_5_0": {
"extensible": true "required": [
"foo"
],
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -2,12 +2,17 @@
{ {
"description": "uniqueItems validation", "description": "uniqueItems validation",
"database": { "database": {
"schemas": { "types": [
"uniqueItems_0_0": { {
"uniqueItems": true, "name": "uniqueItems_0_0",
"extensible": true "schemas": {
"uniqueItems_0_0": {
"uniqueItems": true,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -468,20 +473,25 @@
{ {
"description": "uniqueItems with an array of items", "description": "uniqueItems with an array of items",
"database": { "database": {
"schemas": { "types": [
"uniqueItems_1_0": { {
"prefixItems": [ "name": "uniqueItems_1_0",
{ "schemas": {
"type": "boolean" "uniqueItems_1_0": {
}, "prefixItems": [
{ {
"type": "boolean" "type": "boolean"
},
{
"type": "boolean"
}
],
"uniqueItems": true,
"extensible": true
} }
], }
"uniqueItems": true,
"extensible": true
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -593,20 +603,25 @@
{ {
"description": "uniqueItems with an array of items and additionalItems=false", "description": "uniqueItems with an array of items and additionalItems=false",
"database": { "database": {
"schemas": { "types": [
"uniqueItems_2_0": { {
"prefixItems": [ "name": "uniqueItems_2_0",
{ "schemas": {
"type": "boolean" "uniqueItems_2_0": {
}, "prefixItems": [
{ {
"type": "boolean" "type": "boolean"
},
{
"type": "boolean"
}
],
"uniqueItems": true,
"items": false
} }
], }
"uniqueItems": true,
"items": false
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -675,12 +690,17 @@
{ {
"description": "uniqueItems=false validation", "description": "uniqueItems=false validation",
"database": { "database": {
"schemas": { "types": [
"uniqueItems_3_0": { {
"uniqueItems": false, "name": "uniqueItems_3_0",
"extensible": true "schemas": {
"uniqueItems_3_0": {
"uniqueItems": false,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -920,20 +940,25 @@
{ {
"description": "uniqueItems=false with an array of items", "description": "uniqueItems=false with an array of items",
"database": { "database": {
"schemas": { "types": [
"uniqueItems_4_0": { {
"prefixItems": [ "name": "uniqueItems_4_0",
{ "schemas": {
"type": "boolean" "uniqueItems_4_0": {
}, "prefixItems": [
{ {
"type": "boolean" "type": "boolean"
},
{
"type": "boolean"
}
],
"uniqueItems": false,
"extensible": true
} }
], }
"uniqueItems": false,
"extensible": true
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -1045,20 +1070,25 @@
{ {
"description": "uniqueItems=false with an array of items and additionalItems=false", "description": "uniqueItems=false with an array of items and additionalItems=false",
"database": { "database": {
"schemas": { "types": [
"uniqueItems_5_0": { {
"prefixItems": [ "name": "uniqueItems_5_0",
{ "schemas": {
"type": "boolean" "uniqueItems_5_0": {
}, "prefixItems": [
{ {
"type": "boolean" "type": "boolean"
},
{
"type": "boolean"
}
],
"uniqueItems": false,
"items": false
} }
], }
"uniqueItems": false,
"items": false
} }
} ]
}, },
"tests": [ "tests": [
{ {
@ -1127,12 +1157,17 @@
{ {
"description": "extensible: true allows extra items in uniqueItems", "description": "extensible: true allows extra items in uniqueItems",
"database": { "database": {
"schemas": { "types": [
"uniqueItems_6_0": { {
"uniqueItems": true, "name": "uniqueItems_6_0",
"extensible": true "schemas": {
"uniqueItems_6_0": {
"uniqueItems": true,
"extensible": true
}
}
} }
} ]
}, },
"tests": [ "tests": [
{ {

View File

@ -8,7 +8,7 @@ impl Schema {
pub fn compile_filter( pub fn compile_filter(
&self, &self,
_db: &Database, _db: &Database,
_root_id: &str, root_id: &str,
_errors: &mut Vec<crate::drop::Error>, _errors: &mut Vec<crate::drop::Error>,
) -> Option<Schema> { ) -> Option<Schema> {
if let Some(props) = self.obj.compiled_properties.get() { if let Some(props) = self.obj.compiled_properties.get() {
@ -31,9 +31,51 @@ impl Schema {
} }
if !filter_props.is_empty() { if !filter_props.is_empty() {
let root_filter_type = format!("{}.filter", root_id);
let mut and_obj = SchemaObject::default();
and_obj.type_ = Some(SchemaTypeOrArray::Multiple(vec![
"array".to_string(),
"null".to_string(),
]));
and_obj.items = Some(Arc::new(Schema {
obj: SchemaObject {
type_: Some(SchemaTypeOrArray::Single(root_filter_type.clone())),
..Default::default()
},
always_fail: false,
}));
filter_props.insert(
"$and".to_string(),
Arc::new(Schema {
obj: and_obj,
always_fail: false,
}),
);
let mut or_obj = SchemaObject::default();
or_obj.type_ = Some(SchemaTypeOrArray::Multiple(vec![
"array".to_string(),
"null".to_string(),
]));
or_obj.items = Some(Arc::new(Schema {
obj: SchemaObject {
type_: Some(SchemaTypeOrArray::Single(root_filter_type.clone())),
..Default::default()
},
always_fail: false,
}));
filter_props.insert(
"$or".to_string(),
Arc::new(Schema {
obj: or_obj,
always_fail: false,
}),
);
let mut wrapper_obj = SchemaObject::default(); let mut wrapper_obj = SchemaObject::default();
// Conceptually link this directly into the STI lineage of the base `filter` object // Filters are just plain objects containing conditions, no inheritance required
wrapper_obj.type_ = Some(SchemaTypeOrArray::Single("filter".to_string())); wrapper_obj.type_ = Some(SchemaTypeOrArray::Single("object".to_string()));
wrapper_obj.properties = Some(filter_props); wrapper_obj.properties = Some(filter_props);
return Some(Schema { return Some(Schema {

View File

@ -51,8 +51,8 @@ impl Schema {
// 1. Resolve INHERITANCE dependencies first // 1. Resolve INHERITANCE dependencies first
if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &self.obj.type_ { if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &self.obj.type_ {
if !crate::database::object::is_primitive_type(t) { if !crate::database::object::is_primitive_type(t) && !t.starts_with('$') {
if let Some(parent) = db.schemas.get(t) { if let Some(parent) = db.schemas.get(t).cloned() {
parent.as_ref().compile(db, t, t.clone(), errors); parent.as_ref().compile(db, t, t.clone(), errors);
if let Some(p_props) = parent.obj.compiled_properties.get() { if let Some(p_props) = parent.obj.compiled_properties.get() {
props.extend(p_props.clone()); props.extend(p_props.clone());
@ -85,8 +85,8 @@ impl Schema {
} }
for t in types { for t in types {
if !crate::database::object::is_primitive_type(t) { if !crate::database::object::is_primitive_type(t) && !t.starts_with('$') {
if let Some(parent) = db.schemas.get(t) { if let Some(parent) = db.schemas.get(t).cloned() {
parent.as_ref().compile(db, t, t.clone(), errors); parent.as_ref().compile(db, t, t.clone(), errors);
} }
} }

View File

@ -12,7 +12,10 @@ impl Schema {
let mut strategy = String::new(); let mut strategy = String::new();
if let Some(family) = &self.obj.family { if let Some(family) = &self.obj.family {
// Formalize the <Variant>.<Base> topology
// family_base extracts the 'Base' (e.g. 'widget', 'person')
let family_base = family.split('.').next_back().unwrap_or(family).to_string(); let family_base = family.split('.').next_back().unwrap_or(family).to_string();
// family_prefix extracts the 'Variant' (e.g. 'stock', 'light')
let family_prefix = family let family_prefix = family
.strip_suffix(&family_base) .strip_suffix(&family_base)
.unwrap_or("") .unwrap_or("")
@ -29,7 +32,7 @@ impl Schema {
format!("{}.{}", family_prefix, var) format!("{}.{}", family_prefix, var)
}; };
if db.schemas.contains_key(&target_id) { if db.schemas.get(&target_id).is_some() {
options.insert(var.to_string(), (None, Some(target_id))); options.insert(var.to_string(), (None, Some(target_id)));
} }
} }

View File

@ -10,8 +10,6 @@ pub mod relation;
pub mod schema; pub mod schema;
pub mod r#type; pub mod r#type;
// External mock exports inside the executor sub-folder
use r#enum::Enum; use r#enum::Enum;
use executors::DatabaseExecutor; use executors::DatabaseExecutor;
@ -157,26 +155,6 @@ impl Database {
} }
} }
if let Some(map) = val.get("schemas").and_then(|v| v.as_object()) {
for (key, item) in map.iter() {
match serde_json::from_value::<Schema>(item.clone()) {
Ok(schema) => {
db.schemas.insert(key.clone(), Arc::new(schema));
}
Err(e) => {
errors.push(crate::drop::Error {
code: "DATABASE_SCHEMA_PARSE_FAILED".to_string(),
message: format!("Failed to parse database schema key '{}': {}", key, e),
details: crate::drop::ErrorDetails {
context: Some(serde_json::json!(key)),
..Default::default()
},
});
}
}
}
}
db.compile(&mut errors); db.compile(&mut errors);
let drop = if errors.is_empty() { let drop = if errors.is_empty() {
crate::drop::Drop::success() crate::drop::Drop::success()
@ -213,30 +191,32 @@ impl Database {
} }
pub fn compile(&mut self, errors: &mut Vec<crate::drop::Error>) { pub fn compile(&mut self, errors: &mut Vec<crate::drop::Error>) {
// Collect existing schemas patched in the databse
let mut harvested = Vec::new();
for (id, schema_arc) in &self.schemas {
crate::database::schema::Schema::collect_schemas(
schema_arc,
id,
id.clone(),
&mut harvested,
errors,
);
}
for (id, schema_arc) in harvested {
self.schemas.insert(id, schema_arc);
}
self.collect_schemas(errors); self.collect_schemas(errors);
// Mathematically evaluate all property inheritances, formats, schemas, and foreign key edges topographically over OnceLocks // Formally evaluate properties with strict 3-pass Ordered Graph execution natively
for (id, schema_arc) in &self.schemas { for (_, enum_def) in &self.enums {
// First compile pass initializes exact structural root_id mapping to resolve DB constraints for (schema_id, schema_arc) in &enum_def.schemas {
let root_id = id.split('/').next().unwrap_or(id); let root_id = schema_id.split('/').next().unwrap_or(schema_id);
schema_arc schema_arc
.as_ref() .as_ref()
.compile(self, root_id, id.clone(), errors); .compile(self, root_id, schema_id.clone(), errors);
}
}
for (_, type_def) in &self.types {
for (schema_id, schema_arc) in &type_def.schemas {
let root_id = schema_id.split('/').next().unwrap_or(schema_id);
schema_arc
.as_ref()
.compile(self, root_id, schema_id.clone(), errors);
}
}
for (_, punc_def) in &self.puncs {
for (schema_id, schema_arc) in &punc_def.schemas {
let root_id = schema_id.split('/').next().unwrap_or(schema_id);
schema_arc
.as_ref()
.compile(self, root_id, schema_id.clone(), errors);
}
} }
// Phase 2: Synthesize Composed Filter References // Phase 2: Synthesize Composed Filter References
@ -260,7 +240,7 @@ impl Database {
let mut filter_ids = Vec::new(); let mut filter_ids = Vec::new();
for (type_name, id, filter_arc) in filter_schemas { for (type_name, id, filter_arc) in filter_schemas {
filter_ids.push(id.clone()); filter_ids.push((type_name.clone(), id.clone()));
self.schemas.insert(id.clone(), filter_arc.clone()); self.schemas.insert(id.clone(), filter_arc.clone());
if let Some(t) = self.types.get_mut(&type_name) { if let Some(t) = self.types.get_mut(&type_name) {
t.schemas.insert(id, filter_arc); t.schemas.insert(id, filter_arc);
@ -268,8 +248,13 @@ impl Database {
} }
// Now actively compile the newly injected filters to lock all nested compose references natively // Now actively compile the newly injected filters to lock all nested compose references natively
for id in filter_ids { for (type_name, id) in filter_ids {
if let Some(filter_arc) = self.schemas.get(&id).cloned() { if let Some(filter_arc) = self
.types
.get(&type_name)
.and_then(|t| t.schemas.get(&id))
.cloned()
{
let root_id = id.split('/').next().unwrap_or(&id); let root_id = id.split('/').next().unwrap_or(&id);
filter_arc filter_arc
.as_ref() .as_ref()
@ -282,13 +267,12 @@ impl Database {
let mut type_insert = Vec::new(); let mut type_insert = Vec::new();
let mut punc_insert = Vec::new(); let mut punc_insert = Vec::new();
let mut enum_insert = Vec::new(); let mut enum_insert = Vec::new();
let mut global_insert = Vec::new();
// Pass 1: Extract all Schemas structurally off top level definitions into the master registry. // Pass 1: Extract all Schemas structurally off top level definitions into the master registry.
// Validate every node recursively via string filters natively! // Validate every node recursively via string filters natively!
for (type_name, type_def) in &self.types { for (type_name, type_def) in &self.types {
for (id, schema_arc) in &type_def.schemas { for (id, schema_arc) in &type_def.schemas {
global_insert.push((id.clone(), Arc::clone(schema_arc))); self.schemas.insert(id.clone(), Arc::clone(schema_arc));
let mut local_insert = Vec::new(); let mut local_insert = Vec::new();
crate::database::schema::Schema::collect_schemas( crate::database::schema::Schema::collect_schemas(
schema_arc, schema_arc,
@ -299,14 +283,13 @@ impl Database {
); );
for entry in &local_insert { for entry in &local_insert {
type_insert.push((type_name.clone(), entry.0.clone(), Arc::clone(&entry.1))); type_insert.push((type_name.clone(), entry.0.clone(), Arc::clone(&entry.1)));
global_insert.push((entry.0.clone(), Arc::clone(&entry.1)));
} }
} }
} }
for (punc_name, punc_def) in &self.puncs { for (punc_name, punc_def) in &self.puncs {
for (id, schema_arc) in &punc_def.schemas { for (id, schema_arc) in &punc_def.schemas {
global_insert.push((id.clone(), Arc::clone(schema_arc))); self.schemas.insert(id.clone(), Arc::clone(schema_arc));
let mut local_insert = Vec::new(); let mut local_insert = Vec::new();
crate::database::schema::Schema::collect_schemas( crate::database::schema::Schema::collect_schemas(
schema_arc, schema_arc,
@ -317,14 +300,13 @@ impl Database {
); );
for entry in &local_insert { for entry in &local_insert {
punc_insert.push((punc_name.clone(), entry.0.clone(), Arc::clone(&entry.1))); punc_insert.push((punc_name.clone(), entry.0.clone(), Arc::clone(&entry.1)));
global_insert.push((entry.0.clone(), Arc::clone(&entry.1)));
} }
} }
} }
for (enum_name, enum_def) in &self.enums { for (enum_name, enum_def) in &self.enums {
for (id, schema_arc) in &enum_def.schemas { for (id, schema_arc) in &enum_def.schemas {
global_insert.push((id.clone(), Arc::clone(schema_arc))); self.schemas.insert(id.clone(), Arc::clone(schema_arc));
let mut local_insert = Vec::new(); let mut local_insert = Vec::new();
crate::database::schema::Schema::collect_schemas( crate::database::schema::Schema::collect_schemas(
schema_arc, schema_arc,
@ -335,34 +317,32 @@ impl Database {
); );
for entry in &local_insert { for entry in &local_insert {
enum_insert.push((enum_name.clone(), entry.0.clone(), Arc::clone(&entry.1))); enum_insert.push((enum_name.clone(), entry.0.clone(), Arc::clone(&entry.1)));
global_insert.push((entry.0.clone(), Arc::clone(&entry.1)));
} }
} }
} }
// Apply global inserts // Apply local scopes and global schema map
for (id, schema_arc) in global_insert {
self.schemas.insert(id, schema_arc);
}
// Apply local scopes
for (origin_name, id, schema_arc) in type_insert { for (origin_name, id, schema_arc) in type_insert {
self.schemas.insert(id.clone(), schema_arc.clone());
if let Some(t) = self.types.get_mut(&origin_name) { if let Some(t) = self.types.get_mut(&origin_name) {
t.schemas.insert(id, schema_arc); t.schemas.insert(id, schema_arc);
} }
} }
for (origin_name, id, schema_arc) in punc_insert { for (origin_name, id, schema_arc) in punc_insert {
self.schemas.insert(id.clone(), schema_arc.clone());
if let Some(p) = self.puncs.get_mut(&origin_name) { if let Some(p) = self.puncs.get_mut(&origin_name) {
p.schemas.insert(id, schema_arc); p.schemas.insert(id, schema_arc);
} }
} }
for (origin_name, id, schema_arc) in enum_insert { for (origin_name, id, schema_arc) in enum_insert {
self.schemas.insert(id.clone(), schema_arc.clone());
if let Some(e) = self.enums.get_mut(&origin_name) { if let Some(e) = self.enums.get_mut(&origin_name) {
e.schemas.insert(id, schema_arc); e.schemas.insert(id, schema_arc);
} }
} }
} }
/// Inspects the Postgres pg_constraint relations catalog to securely identify /// Inspects the Postgres pg_constraint relations catalog to securely identify
/// the precise Foreign Key connecting a parent and child hierarchy path. /// the precise Foreign Key connecting a parent and child hierarchy path.
pub fn resolve_relation<'a>( pub fn resolve_relation<'a>(

View File

@ -22,6 +22,27 @@ impl std::ops::DerefMut for Schema {
} }
} }
impl Schema {
/// Returns true if the schema acts purely as a type pointer (composition without overriding constraints)
pub fn is_proxy(&self) -> bool {
self.obj.properties.is_none()
&& self.obj.pattern_properties.is_none()
&& self.obj.additional_properties.is_none()
&& self.obj.required.is_none()
&& self.obj.dependencies.is_none()
&& self.obj.items.is_none()
&& self.obj.prefix_items.is_none()
&& self.obj.contains.is_none()
&& self.obj.format.is_none()
&& self.obj.enum_.is_none()
&& self.obj.const_.is_none()
&& self.obj.cases.is_none()
&& self.obj.one_of.is_none()
&& self.obj.not.is_none()
&& self.obj.family.is_none()
}
}
impl<'de> Deserialize<'de> for Schema { impl<'de> Deserialize<'de> for Schema {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where where

View File

@ -25,7 +25,7 @@ impl Merger {
let mut notifications_queue = Vec::new(); let mut notifications_queue = Vec::new();
let target_schema = match self.db.schemas.get(schema_id) { let target_schema = match self.db.schemas.get(schema_id) {
Some(s) => Arc::clone(s), Some(s) => Arc::clone(&s),
None => { None => {
return crate::drop::Drop::with_errors(vec![crate::drop::Error { return crate::drop::Drop::with_errors(vec![crate::drop::Error {
code: "MERGE_FAILED".to_string(), code: "MERGE_FAILED".to_string(),
@ -144,30 +144,49 @@ impl Merger {
if let Some(v) = val { if let Some(v) = val {
if let Some((idx_opt, target_id_opt)) = options.get(v) { if let Some((idx_opt, target_id_opt)) = options.get(v) {
if let Some(target_id) = target_id_opt { if let Some(target_id) = target_id_opt {
if let Some(target_schema) = self.db.schemas.get(target_id) { if let Some(target_schema) =
schema = Arc::clone(target_schema); self.db.schemas.get(target_id)
{
schema = target_schema.clone();
} else { } else {
return Err(format!("Polymorphic mapped target '{}' not found in database registry", target_id)); return Err(format!(
"Polymorphic mapped target '{}' not found in database registry",
target_id
));
} }
} else if let Some(idx) = idx_opt { } else if let Some(idx) = idx_opt {
if let Some(target_schema) = schema.obj.one_of.as_ref().and_then(|options| options.get(*idx)) { if let Some(target_schema) = schema
schema = Arc::clone(target_schema); .obj
.one_of
.as_ref()
.and_then(|options| options.get(*idx))
{
schema = Arc::clone(target_schema);
} else { } else {
return Err(format!("Polymorphic index target '{}' not found in local oneOf array", idx)); return Err(format!(
"Polymorphic index target '{}' not found in local oneOf array",
idx
));
} }
} else { } else {
return Err(format!("Polymorphic mapped target has no path")); return Err(format!("Polymorphic mapped target has no path"));
} }
} else { } else {
return Err(format!("Polymorphic discriminator {}='{}' matched no compiled options", disc, v)); return Err(format!(
"Polymorphic discriminator {}='{}' matched no compiled options",
disc, v
));
} }
} else { } else {
return Err(format!("Polymorphic merging failed: missing required discriminator '{}'", disc)); return Err(format!(
"Polymorphic merging failed: missing required discriminator '{}'",
disc
));
} }
} }
} }
self.merge_object(schema, map, notifications) self.merge_object(schema, map, notifications)
}, }
_ => Err("Invalid merge payload: root must be an Object or Array".to_string()), _ => Err("Invalid merge payload: root must be an Object or Array".to_string()),
} }
} }

View File

@ -28,9 +28,10 @@ impl<'a> Compiler<'a> {
.db .db
.schemas .schemas
.get(schema_id) .get(schema_id)
.cloned()
.ok_or_else(|| format!("Schema not found: {}", schema_id))?; .ok_or_else(|| format!("Schema not found: {}", schema_id))?;
let target_schema = std::sync::Arc::clone(schema); let target_schema = schema;
let mut compiler = Compiler { let mut compiler = Compiler {
db: &self.db, db: &self.db,
@ -151,9 +152,9 @@ impl<'a> Compiler<'a> {
if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &node.schema.obj.type_ { if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &node.schema.obj.type_ {
if !crate::database::object::is_primitive_type(t) { if !crate::database::object::is_primitive_type(t) {
// If it's just an ad-hoc struct ref, we should resolve it // If it's just an ad-hoc struct ref, we should resolve it
if let Some(target_schema) = self.db.schemas.get(t) { if let Some(target_schema) = self.db.schemas.get(t).cloned() {
let mut ref_node = node.clone(); let mut ref_node = node.clone();
ref_node.schema = Arc::clone(target_schema); ref_node.schema = target_schema.clone();
ref_node.schema_id = Some(t.clone()); ref_node.schema_id = Some(t.clone());
return self.compile_node(ref_node); return self.compile_node(ref_node);
} }
@ -306,9 +307,9 @@ impl<'a> Compiler<'a> {
for (disc_val, (idx_opt, target_id_opt)) in options { for (disc_val, (idx_opt, target_id_opt)) in options {
if let Some(target_id) = target_id_opt { if let Some(target_id) = target_id_opt {
if let Some(target_schema) = self.db.schemas.get(target_id) { if let Some(target_schema) = self.db.schemas.get(target_id).cloned() {
let mut child_node = node.clone(); let mut child_node = node.clone();
child_node.schema = Arc::clone(target_schema); child_node.schema = target_schema.clone();
child_node.schema_id = Some(target_id.clone()); child_node.schema_id = Some(target_id.clone());
child_node.is_polymorphic_branch = true; child_node.is_polymorphic_branch = true;

View File

@ -1247,6 +1247,36 @@ fn test_const_17_1() {
crate::tests::runner::run_test_case(&path, 17, 1).unwrap(); crate::tests::runner::run_test_case(&path, 17, 1).unwrap();
} }
#[test]
fn test_dynamic_type_0_0() {
let path = format!("{}/fixtures/dynamicType.json", env!("CARGO_MANIFEST_DIR"));
crate::tests::runner::run_test_case(&path, 0, 0).unwrap();
}
#[test]
fn test_dynamic_type_0_1() {
let path = format!("{}/fixtures/dynamicType.json", env!("CARGO_MANIFEST_DIR"));
crate::tests::runner::run_test_case(&path, 0, 1).unwrap();
}
#[test]
fn test_dynamic_type_0_2() {
let path = format!("{}/fixtures/dynamicType.json", env!("CARGO_MANIFEST_DIR"));
crate::tests::runner::run_test_case(&path, 0, 2).unwrap();
}
#[test]
fn test_dynamic_type_0_3() {
let path = format!("{}/fixtures/dynamicType.json", env!("CARGO_MANIFEST_DIR"));
crate::tests::runner::run_test_case(&path, 0, 3).unwrap();
}
#[test]
fn test_dynamic_type_0_4() {
let path = format!("{}/fixtures/dynamicType.json", env!("CARGO_MANIFEST_DIR"));
crate::tests::runner::run_test_case(&path, 0, 4).unwrap();
}
#[test] #[test]
fn test_property_names_0_0() { fn test_property_names_0_0() {
let path = format!("{}/fixtures/propertyNames.json", env!("CARGO_MANIFEST_DIR")); let path = format!("{}/fixtures/propertyNames.json", env!("CARGO_MANIFEST_DIR"));

View File

@ -140,13 +140,27 @@ fn test_library_api() {
"type": "object" "type": "object"
}, },
"source_schema.filter": { "source_schema.filter": {
"compiledPropertyNames": ["name", "target", "type"], "compiledPropertyNames": ["$and", "$or", "name", "target", "type"],
"properties": { "properties": {
"$and": {
"type": ["array", "null"],
"items": {
"compiledPropertyNames": ["$and", "$or", "name", "target", "type"],
"type": "source_schema.filter"
}
},
"$or": {
"type": ["array", "null"],
"items": {
"compiledPropertyNames": ["$and", "$or", "name", "target", "type"],
"type": "source_schema.filter"
}
},
"name": { "type": ["string.condition", "null"] }, "name": { "type": ["string.condition", "null"] },
"target": { "type": ["target_schema.filter", "null"] }, "target": { "type": ["target_schema.filter", "null"] },
"type": { "type": ["string.condition", "null"] } "type": { "type": ["string.condition", "null"] }
}, },
"type": "filter" "type": "object"
} }
}, },
"sensitive": false, "sensitive": false,
@ -179,11 +193,25 @@ fn test_library_api() {
"type": "object" "type": "object"
}, },
"target_schema.filter": { "target_schema.filter": {
"compiledPropertyNames": ["value"], "compiledPropertyNames": ["$and", "$or", "value"],
"properties": { "properties": {
"$and": {
"type": ["array", "null"],
"items": {
"compiledPropertyNames": ["$and", "$or", "value"],
"type": "target_schema.filter"
}
},
"$or": {
"type": ["array", "null"],
"items": {
"compiledPropertyNames": ["$and", "$or", "value"],
"type": "target_schema.filter"
}
},
"value": { "type": ["number.condition", "null"] } "value": { "type": ["number.condition", "null"] }
}, },
"type": "filter" "type": "object"
} }
}, },
"sensitive": false, "sensitive": false,

View File

@ -64,12 +64,6 @@ impl Case {
let validator = Validator::new(db); let validator = Validator::new(db);
let schema_id = &self.schema_id; let schema_id = &self.schema_id;
if !validator.db.schemas.contains_key(schema_id) {
return Err(format!(
"Missing Schema: Cannot find schema ID '{}'",
schema_id
));
}
let test_data = self.data.clone().unwrap_or(Value::Null); let test_data = self.data.clone().unwrap_or(Value::Null);
let result = validator.validate(schema_id, &test_data); let result = validator.validate(schema_id, &test_data);

View File

@ -5,7 +5,16 @@ impl Expect {
pub fn assert_schemas(&self, db: &Arc<crate::database::Database>) -> Result<(), String> { pub fn assert_schemas(&self, db: &Arc<crate::database::Database>) -> Result<(), String> {
if let Some(expected_map) = &self.schemas { if let Some(expected_map) = &self.schemas {
// Collect actual schemas and sort // Collect actual schemas and sort
let mut actual: Vec<String> = db.schemas.keys().cloned().collect(); let mut actual: Vec<String> = Vec::new();
for type_def in db.types.values() {
actual.extend(type_def.schemas.keys().cloned());
}
for punc_def in db.puncs.values() {
actual.extend(punc_def.schemas.keys().cloned());
}
for enum_def in db.enums.values() {
actual.extend(enum_def.schemas.keys().cloned());
}
actual.sort(); actual.sort();
// Collect expected schemas and sort // Collect expected schemas and sort
@ -26,7 +35,7 @@ impl Expect {
if expected_val.is_object() && expected_val.as_object().unwrap().is_empty() { if expected_val.is_object() && expected_val.as_object().unwrap().is_empty() {
continue; // A `{}` means we just wanted to test it was collected/promoted, skip deep match continue; // A `{}` means we just wanted to test it was collected/promoted, skip deep match
} }
let actual_ast = db.schemas.get(key).unwrap(); let actual_ast = db.schemas.get(key).cloned().unwrap();
let actual_val = serde_json::to_value(actual_ast).unwrap(); let actual_val = serde_json::to_value(actual_ast).unwrap();
if actual_val != *expected_val { if actual_val != *expected_val {

View File

@ -15,6 +15,7 @@ pub struct ValidationContext<'a> {
pub extensible: bool, pub extensible: bool,
pub reporter: bool, pub reporter: bool,
pub overrides: HashSet<String>, pub overrides: HashSet<String>,
pub parent: Option<&'a serde_json::Value>,
} }
impl<'a> ValidationContext<'a> { impl<'a> ValidationContext<'a> {
@ -38,6 +39,7 @@ impl<'a> ValidationContext<'a> {
extensible: effective_extensible, extensible: effective_extensible,
reporter, reporter,
overrides, overrides,
parent: None,
} }
} }
@ -57,6 +59,7 @@ impl<'a> ValidationContext<'a> {
overrides: HashSet<String>, overrides: HashSet<String>,
extensible: bool, extensible: bool,
reporter: bool, reporter: bool,
parent_instance: Option<&'a serde_json::Value>,
) -> Self { ) -> Self {
let effective_extensible = schema.extensible.unwrap_or(extensible); let effective_extensible = schema.extensible.unwrap_or(extensible);
@ -70,6 +73,7 @@ impl<'a> ValidationContext<'a> {
extensible: effective_extensible, extensible: effective_extensible,
reporter, reporter,
overrides, overrides,
parent: parent_instance,
} }
} }
@ -81,6 +85,7 @@ impl<'a> ValidationContext<'a> {
HashSet::new(), HashSet::new(),
self.extensible, self.extensible,
reporter, reporter,
self.parent,
) )
} }

View File

@ -23,10 +23,6 @@ impl Validator {
Self { db } Self { db }
} }
pub fn get_schema_ids(&self) -> Vec<String> {
self.db.schemas.keys().cloned().collect()
}
pub fn check_type(t: &str, val: &Value) -> bool { pub fn check_type(t: &str, val: &Value) -> bool {
if let Value::String(s) = val if let Value::String(s) = val
&& s.is_empty() && s.is_empty()
@ -46,11 +42,13 @@ impl Validator {
} }
pub fn validate(&self, schema_id: &str, instance: &Value) -> crate::drop::Drop { pub fn validate(&self, schema_id: &str, instance: &Value) -> crate::drop::Drop {
if let Some(schema) = self.db.schemas.get(schema_id) { let schema_opt = self.db.schemas.get(schema_id);
if let Some(schema) = schema_opt {
let ctx = ValidationContext::new( let ctx = ValidationContext::new(
&self.db, &self.db,
schema, &schema,
schema, &schema,
instance, instance,
HashSet::new(), HashSet::new(),
false, false,

View File

@ -57,6 +57,7 @@ impl<'a> ValidationContext<'a> {
HashSet::new(), HashSet::new(),
self.extensible, self.extensible,
false, false,
Some(self.instance),
); );
let check = derived.validate()?; let check = derived.validate()?;
@ -108,6 +109,7 @@ impl<'a> ValidationContext<'a> {
HashSet::new(), HashSet::new(),
self.extensible, self.extensible,
false, false,
Some(self.instance),
); );
let item_res = derived.validate()?; let item_res = derived.validate()?;
result.merge(item_res); result.merge(item_res);
@ -137,6 +139,7 @@ impl<'a> ValidationContext<'a> {
HashSet::new(), HashSet::new(),
self.extensible, self.extensible,
false, false,
Some(self.instance),
); );
let item_res = derived.validate()?; let item_res = derived.validate()?;
result.merge(item_res); result.merge(item_res);

View File

@ -12,6 +12,7 @@ pub mod numeric;
pub mod object; pub mod object;
pub mod polymorphism; pub mod polymorphism;
pub mod string; pub mod string;
pub mod r#type;
pub mod util; pub mod util;
impl<'a> ValidationContext<'a> { impl<'a> ValidationContext<'a> {
@ -28,7 +29,7 @@ impl<'a> ValidationContext<'a> {
if !self.validate_family(&mut result)? { if !self.validate_family(&mut result)? {
return Ok(result); return Ok(result);
} }
if !self.validate_type_inheritance(&mut result)? { if !self.validate_type(&mut result)? {
return Ok(result); return Ok(result);
} }

View File

@ -23,10 +23,13 @@ impl<'a> ValidationContext<'a> {
// Entity implicit type validation // Entity implicit type validation
if let Some(ref schema_identifier_str) = schema_identifier { if let Some(ref schema_identifier_str) = schema_identifier {
// We decompose identity string routing inherently // We decompose identity string routing inherently
let expected_type = schema_identifier_str.split('.').last().unwrap_or(schema_identifier_str); let expected_type = schema_identifier_str
.split('.')
.last()
.unwrap_or(schema_identifier_str);
// Check if the identifier represents a registered global database entity boundary mathematically // Check if the identifier represents a registered global database entity boundary mathematically
if let Some(type_def) = self.db.types.get(expected_type) { if let Some(type_def) = self.db.types.get(expected_type).filter(|t| !t.variations.is_empty()) {
if let Some(type_val) = obj.get("type") { if let Some(type_val) = obj.get("type") {
if let Some(type_str) = type_val.as_str() { if let Some(type_str) = type_val.as_str() {
if type_def.variations.contains(type_str) { if type_def.variations.contains(type_str) {
@ -47,21 +50,28 @@ impl<'a> ValidationContext<'a> {
// Because it's a global entity target, the payload must structurally provide a discriminator natively // Because it's a global entity target, the payload must structurally provide a discriminator natively
result.errors.push(ValidationError { result.errors.push(ValidationError {
code: "MISSING_TYPE".to_string(), code: "MISSING_TYPE".to_string(),
message: format!("Schema mechanically requires type discrimination '{}'", expected_type), message: format!(
"Schema mechanically requires type discrimination '{}'",
expected_type
),
path: self.path.clone(), // Empty boundary path: self.path.clone(), // Empty boundary
}); });
} }
// If the target mathematically declares a horizontal structural STI variation natively // If the target mathematically declares a horizontal structural STI variation natively
if schema_identifier_str.contains('.') { if schema_identifier_str.contains('.') {
let requires_kind = self.schema.compiled_properties.get() let requires_kind = self
.map_or(false, |p| p.contains_key("kind")); .schema
.compiled_properties
.get()
.map_or(false, |p| p.contains_key("kind"));
if requires_kind { if requires_kind {
if obj.get("kind").is_none() { if obj.get("kind").is_none() {
result.errors.push(ValidationError { result.errors.push(ValidationError {
code: "MISSING_KIND".to_string(), code: "MISSING_KIND".to_string(),
message: "Schema mechanically requires horizontal kind discrimination".to_string(), message: "Schema mechanically requires horizontal kind discrimination"
.to_string(),
path: self.path.clone(), path: self.path.clone(),
}); });
} else { } else {
@ -74,20 +84,20 @@ impl<'a> ValidationContext<'a> {
// Because they lack manual type property descriptors, we natively shield "type" and "kind" keys from // Because they lack manual type property descriptors, we natively shield "type" and "kind" keys from
// triggering additionalProperty violations natively IF they precisely correspond to their fast-path boundaries // triggering additionalProperty violations natively IF they precisely correspond to their fast-path boundaries
if let Some(type_val) = obj.get("type") { if let Some(type_val) = obj.get("type") {
if let Some(type_str) = type_val.as_str() { if let Some(type_str) = type_val.as_str() {
if type_str == expected_type { if type_str == expected_type {
result.evaluated_keys.insert("type".to_string()); result.evaluated_keys.insert("type".to_string());
} }
} }
} }
if let Some(kind_val) = obj.get("kind") { if let Some(kind_val) = obj.get("kind") {
if let Some((kind_str, _)) = schema_identifier_str.rsplit_once('.') { if let Some((kind_str, _)) = schema_identifier_str.rsplit_once('.') {
if let Some(actual_kind) = kind_val.as_str() { if let Some(actual_kind) = kind_val.as_str() {
if actual_kind == kind_str { if actual_kind == kind_str {
result.evaluated_keys.insert("kind".to_string()); result.evaluated_keys.insert("kind".to_string());
}
} }
} }
}
} }
} }
} }
@ -167,7 +177,9 @@ impl<'a> ValidationContext<'a> {
if let Some(child_instance) = obj.get(key) { if let Some(child_instance) = obj.get(key) {
let new_path = self.join_path(key); let new_path = self.join_path(key);
let is_ref = match &sub_schema.type_ { let is_ref = match &sub_schema.type_ {
Some(crate::database::object::SchemaTypeOrArray::Single(t)) => !crate::database::object::is_primitive_type(t), Some(crate::database::object::SchemaTypeOrArray::Single(t)) => {
!crate::database::object::is_primitive_type(t)
}
_ => false, _ => false,
}; };
let next_extensible = if is_ref { false } else { self.extensible }; let next_extensible = if is_ref { false } else { self.extensible };
@ -179,11 +191,10 @@ impl<'a> ValidationContext<'a> {
HashSet::new(), HashSet::new(),
next_extensible, next_extensible,
false, false,
Some(self.instance),
); );
let item_res = derived.validate()?; let item_res = derived.validate()?;
result.merge(item_res); result.merge(item_res);
result.evaluated_keys.insert(key.to_string()); result.evaluated_keys.insert(key.to_string());
} }
@ -196,7 +207,9 @@ impl<'a> ValidationContext<'a> {
if compiled_re.0.is_match(key) { if compiled_re.0.is_match(key) {
let new_path = self.join_path(key); let new_path = self.join_path(key);
let is_ref = match &sub_schema.type_ { let is_ref = match &sub_schema.type_ {
Some(crate::database::object::SchemaTypeOrArray::Single(t)) => !crate::database::object::is_primitive_type(t), Some(crate::database::object::SchemaTypeOrArray::Single(t)) => {
!crate::database::object::is_primitive_type(t)
}
_ => false, _ => false,
}; };
let next_extensible = if is_ref { false } else { self.extensible }; let next_extensible = if is_ref { false } else { self.extensible };
@ -208,6 +221,7 @@ impl<'a> ValidationContext<'a> {
HashSet::new(), HashSet::new(),
next_extensible, next_extensible,
false, false,
Some(self.instance),
); );
let item_res = derived.validate()?; let item_res = derived.validate()?;
result.merge(item_res); result.merge(item_res);
@ -225,7 +239,8 @@ impl<'a> ValidationContext<'a> {
{ {
locally_matched = true; locally_matched = true;
} }
if !locally_matched && let Some(compiled_pp) = self.schema.compiled_pattern_properties.get() if !locally_matched
&& let Some(compiled_pp) = self.schema.compiled_pattern_properties.get()
{ {
for (compiled_re, _) in compiled_pp { for (compiled_re, _) in compiled_pp {
if compiled_re.0.is_match(key) { if compiled_re.0.is_match(key) {
@ -238,7 +253,9 @@ impl<'a> ValidationContext<'a> {
if !locally_matched { if !locally_matched {
let new_path = self.join_path(key); let new_path = self.join_path(key);
let is_ref = match &additional_schema.type_ { let is_ref = match &additional_schema.type_ {
Some(crate::database::object::SchemaTypeOrArray::Single(t)) => !crate::database::object::is_primitive_type(t), Some(crate::database::object::SchemaTypeOrArray::Single(t)) => {
!crate::database::object::is_primitive_type(t)
}
_ => false, _ => false,
}; };
let next_extensible = if is_ref { false } else { self.extensible }; let next_extensible = if is_ref { false } else { self.extensible };
@ -250,6 +267,7 @@ impl<'a> ValidationContext<'a> {
HashSet::new(), HashSet::new(),
next_extensible, next_extensible,
false, false,
Some(self.instance),
); );
let item_res = derived.validate()?; let item_res = derived.validate()?;
result.merge(item_res); result.merge(item_res);

View File

@ -100,7 +100,7 @@ impl<'a> ValidationContext<'a> {
if let Some((idx_opt, target_id_opt)) = options.get(&val) { if let Some((idx_opt, target_id_opt)) = options.get(&val) {
if let Some(target_id) = target_id_opt { if let Some(target_id) = target_id_opt {
if let Some(target_schema) = self.db.schemas.get(target_id) { if let Some(target_schema) = self.db.schemas.get(target_id) {
let derived = self.derive_for_schema(target_schema.as_ref(), false); let derived = self.derive_for_schema(target_schema, false);
let sub_res = derived.validate()?; let sub_res = derived.validate()?;
let is_valid = sub_res.is_valid(); let is_valid = sub_res.is_valid();
result.merge(sub_res); result.merge(sub_res);
@ -176,78 +176,4 @@ impl<'a> ValidationContext<'a> {
return Ok(false); return Ok(false);
} }
} }
pub(crate) fn validate_type_inheritance(
&self,
result: &mut ValidationResult,
) -> Result<bool, ValidationError> {
// Core inheritance logic replaces legacy routing
let payload_primitive = match self.instance {
serde_json::Value::Null => "null",
serde_json::Value::Bool(_) => "boolean",
serde_json::Value::Number(n) => {
if n.is_i64() || n.is_u64() {
"integer"
} else {
"number"
}
}
serde_json::Value::String(_) => "string",
serde_json::Value::Array(_) => "array",
serde_json::Value::Object(_) => "object",
};
let mut custom_types = Vec::new();
match &self.schema.type_ {
Some(crate::database::object::SchemaTypeOrArray::Single(t)) => {
if !crate::database::object::is_primitive_type(t) {
custom_types.push(t.clone());
}
}
Some(crate::database::object::SchemaTypeOrArray::Multiple(arr)) => {
if arr.contains(&payload_primitive.to_string())
|| (payload_primitive == "integer" && arr.contains(&"number".to_string()))
{
// It natively matched a primitive in the array options, skip forcing custom proxy fallback
} else {
for t in arr {
if !crate::database::object::is_primitive_type(t) {
custom_types.push(t.clone());
}
}
}
}
None => {}
}
for t in custom_types {
if let Some(global_schema) = self.db.schemas.get(&t) {
let mut new_overrides = self.overrides.clone();
if let Some(props) = &self.schema.properties {
new_overrides.extend(props.keys().map(|k| k.to_string()));
}
let mut shadow = self.derive(
global_schema,
self.instance,
&self.path,
new_overrides,
self.extensible,
true, // Reporter mode
);
shadow.root = global_schema;
result.merge(shadow.validate()?);
} else {
result.errors.push(ValidationError {
code: "INHERITANCE_RESOLUTION_FAILED".to_string(),
message: format!(
"Inherited entity pointer '{}' was not found in schema registry",
t
),
path: self.path.to_string(),
});
}
}
Ok(true)
}
} }

138
src/validator/rules/type.rs Normal file
View File

@ -0,0 +1,138 @@
use crate::validator::context::ValidationContext;
use crate::validator::error::ValidationError;
use crate::validator::result::ValidationResult;
impl<'a> ValidationContext<'a> {
pub(crate) fn validate_type(
&self,
result: &mut ValidationResult,
) -> Result<bool, ValidationError> {
let payload_primitive = match self.instance {
serde_json::Value::Null => "null",
serde_json::Value::Bool(_) => "boolean",
serde_json::Value::Number(n) => {
if n.is_i64() || n.is_u64() {
"integer"
} else {
"number"
}
}
serde_json::Value::String(_) => "string",
serde_json::Value::Array(_) => "array",
serde_json::Value::Object(_) => "object",
};
let mut custom_types = Vec::new();
match &self.schema.type_ {
Some(crate::database::object::SchemaTypeOrArray::Single(t)) => {
if !crate::database::object::is_primitive_type(t) {
custom_types.push(t.clone());
}
}
Some(crate::database::object::SchemaTypeOrArray::Multiple(arr)) => {
if arr.contains(&payload_primitive.to_string())
|| (payload_primitive == "integer" && arr.contains(&"number".to_string()))
{
// It natively matched a primitive in the array options, skip forcing custom proxy fallback
} else {
for t in arr {
if !crate::database::object::is_primitive_type(t) {
custom_types.push(t.clone());
}
}
}
}
None => {}
}
for t in custom_types {
let mut target_id = t.clone();
// 1. DYNAMIC TYPE (Composition)
if t.starts_with('$') {
let parts: Vec<&str> = t.split('.').collect();
let var_name = &parts[0][1..]; // Remove the $ prefix
let suffix = if parts.len() > 1 {
format!(".{}", parts[1..].join("."))
} else {
String::new()
};
let mut resolved = false;
if let Some(parent) = self.parent {
if let Some(obj) = parent.as_object() {
if let Some(val) = obj.get(var_name) {
if let Some(str_val) = val.as_str() {
target_id = format!("{}{}", str_val, suffix);
resolved = true;
}
}
}
}
if !resolved {
result.errors.push(ValidationError {
code: "DYNAMIC_TYPE_RESOLUTION_FAILED".to_string(),
message: format!(
"Dynamic type pointer '{}' could not resolve discriminator property '{}' on parent instance",
t, var_name
),
path: self.path.to_string(),
});
continue;
}
}
// 2. Fetch and apply
if let Some(global_schema) = self.db.schemas.get(&target_id) {
let mut new_overrides = self.overrides.clone();
if let Some(props) = &self.schema.properties {
new_overrides.extend(props.keys().map(|k| k.to_string()));
}
let mut shadow = self.derive(
&global_schema,
self.instance,
&self.path,
new_overrides,
self.extensible,
true, // Reporter mode
self.parent,
);
shadow.root = &global_schema;
result.merge(shadow.validate()?);
} else {
// 3. Error handling pathways
if t.starts_with('$') {
result.errors.push(ValidationError {
code: "DYNAMIC_TYPE_RESOLUTION_FAILED".to_string(),
message: format!(
"Resolved dynamic type pointer '{}' was not found in schema registry",
target_id
),
path: self.path.to_string(),
});
} else if self.schema.is_proxy() {
result.errors.push(ValidationError {
code: "PROXY_TYPE_RESOLUTION_FAILED".to_string(),
message: format!(
"Composed proxy entity pointer '{}' was not found in schema registry",
target_id
),
path: self.path.to_string(),
});
} else {
result.errors.push(ValidationError {
code: "INHERITANCE_RESOLUTION_FAILED".to_string(),
message: format!(
"Inherited entity pointer '{}' was not found in schema registry",
target_id
),
path: self.path.to_string(),
});
}
}
}
Ok(true)
}
}

View File

@ -1 +1 @@
1.0.125 1.0.128