significantly simplified the validator and work continues on query
This commit is contained in:
63
GEMINI.md
63
GEMINI.md
@ -67,30 +67,41 @@ Returns a debug dump of the currently cached schemas (for development/debugging)
|
||||
|
||||
## ✨ Custom Features & Deviations
|
||||
|
||||
JSPG implements specific extensions to the Draft 2020-12 standard to support the Punc architecture's object-oriented needs.
|
||||
JSPG implements specific extensions to the Draft 2020-12 standard to support the Punc architecture's object-oriented needs while heavily optimizing for zero-runtime lookups.
|
||||
|
||||
### 1. The Unified Semantic Graph & Native Inheritance
|
||||
JSPG goes beyond Draft 2020-12 to natively understand Object-Oriented inheritance and polymorphism. During the `cache_json_schemas()` phase, JSPG builds a single Directed Acyclic Graph (DAG) using **only** the `$ref` keyword. Every schema that uses `$ref` establishes a parent-to-child relationship.
|
||||
### 1. Polymorphism & Referencing (`$ref`, `$family`, and Native Types)
|
||||
|
||||
Furthermore, `jspg` knows which schemas belong directly to database tables (Entities) versus which are ad-hoc API shapes.
|
||||
* **Native `type` Discrimination**: For any schema that traces its ancestry back to the base `entity`, JSPG securely and implicitly manages the `type` property. You do **not** need to explicitly override `"type": {"const": "person"}` in entity subclasses. If a schema `$ref`s `organization`, JSPG automatically allows the incoming `type` to be anything in the `organization` family tree (e.g., `person`, `bot`), but rigidly truncates/masks the data structure to the requested `organization` shape.
|
||||
* **Ad-Hoc Objects**: If an ad-hoc schema `$ref`s a base object but does not trace back to `entity`, standard JSON Schema rules apply (no magical `type` tracking).
|
||||
JSPG replaces the complex, dynamic reference resolution logic of standard JSON Schema (e.g., `$defs`, relative URIs, `$dynamicRef`, `$dynamicAnchor`, `if/then/else`) with a strict, explicitly structured global `$id` system. This powers predictable code generation and blazing-fast runtime validation.
|
||||
|
||||
> [!NOTE]
|
||||
> **`$ref` never creates a Union.** When you use `$ref`, you are asking for a single, concrete struct/shape. The schema's strict fields will be rigidly enforced, but the `type` property is permitted to match any valid descendant via the native discrimination.
|
||||
#### A. Global `$id` Conventions & Schema Buckets
|
||||
Every schema is part of a flat, globally addressable namespace. However, where a schema is defined in the database determines its physical boundaries:
|
||||
* **Types (Entities)**: Schemas defined within a Postgres `type` represent entities. The `$id` must be exactly the type name (`person`) or suffixed (`full.person`). All schemas in this bucket receive strict Native Type Discrimination based on the physical table hierarchy.
|
||||
* **Puncs (APIs)**: Schemas defined within a `punc` are ad-hoc containers. The `$id` must be exactly `[punc_name].request` or `[punc_name].response`. They are never entities themselves.
|
||||
* **Enums (Domains)**: Schemas defined within an `enum` represent enum definitions. The `$id` must be exactly the enum name (`job_status`) or suffixed (`short.job_status`).
|
||||
|
||||
### 2. Shape Polymorphism & Virtual Unions (`$family`)
|
||||
To support polymorphic API contracts and deeply nested UI Unions without manually writing massive `oneOf` blocks, JSPG provides the `$family` macro. While `$ref` guarantees a single shape, `$family` asks the code generators for a true Polymorphic Union class.
|
||||
#### B. Native Type Discrimination (The `variations` Property)
|
||||
Because `jspg` knows which schemas are Entities based on their origin bucket (Types), it securely and implicitly manages the `"type"` property by attaching `compiled_variations`.
|
||||
If a schema originates in the `user` bucket, the validator does *not* rigidly require `{"type": "user"}`. Instead, it queries the physical Postgres type inheritance graph (e.g. `[entity, organization, user]`) and allows the JSON to be `{"type": "person"}` or `{"type": "bot"}` automatically, enabling seamless API polymorphism.
|
||||
|
||||
When `{"$family": "organization.light"}` is encountered, JSPG:
|
||||
1. Locates the base `organization` node in the Semantic Graph.
|
||||
2. Recursively walks down to find all descendants via `$ref`.
|
||||
3. **Strictly Filters** the descendants using the exact dot-notation suffix requested. It will only include descendants whose `$id` matches the shape modifier (e.g., `person.light`, `user.light`). If `bot` has no `.light` shape defined, it is securely omitted from the union.
|
||||
4. Generates a virtual `oneOf` array containing those precise `$ref`s.
|
||||
#### C. Structural Inheritance & Viral Infection (`$ref`)
|
||||
`$ref` is used exclusively for structural inheritance.
|
||||
* **Viral Infection**: If an anonymous schema or an ad-hoc schema (like a Punc Request) `$ref`s a strict Entity schema (like `person.light`), it *virally inherits* the `compiled_variations` of that target. This means a Punc request instantly gains the exact polymorphic security boundaries of the Entity it points to.
|
||||
* **`$ref` never creates a Union.** When you use `$ref`, you are asking for a single, concrete struct/shape.
|
||||
|
||||
This cleanly separates **Database Ancestry** (managed entirely and implicitly by `$ref` for single shapes) from **Shape Variations** (managed explicitly by `$family` to build `oneOf` unions).
|
||||
#### D. Shape Polymorphism & Virtual Unions (`$family`)
|
||||
To support polymorphic API contracts (e.g., heterogeneous arrays of generic widgets) without manually writing massive `oneOf` blocks, JSPG provides the `$family` macro.
|
||||
While `$ref` defines rigid structure, `$family` relies on an abstract **Descendants Graph**.
|
||||
|
||||
### 3. Strict by Default & Extensibility
|
||||
During compilation, `jspg` temporarily tracks every `$ref` pointer globally to build a reverse-lookup graph of "Descendants".
|
||||
When `{"$family": "widget"}` is encountered, JSPG:
|
||||
1. Locates the `widget` schema in the Descendants graph.
|
||||
2. Expands the macro by finding *every* schema in the entire database that structurally `$ref`s `widget`, directly or indirectly (e.g., `stock.widget`, an anonymous object, etc.).
|
||||
3. Replaces the `$family` keyword with a standard `one_of` array containing `$ref`s to those discovered descendants.
|
||||
|
||||
If you request `{"$family": "light.widget"}`, it simply expands to all schemas that `$ref` the generic abstract `light.widget` interface.
|
||||
This cleanly separates **Database Physics** (derived from the Postgres `Types` bucket and viral `$ref` inheritance) from **Structural Polymorphism** (derived purely from the abstract `$ref` tree).
|
||||
|
||||
### 2. Strict by Default & Extensibility
|
||||
JSPG enforces a "Secure by Default" philosophy. All schemas are treated as if `unevaluatedProperties: false` (and `unevaluatedItems: false`) is set, unless explicitly overridden.
|
||||
|
||||
* **Strictness**: By default, any property or array item in the instance data that is not explicitly defined in the schema causes a validation error. This prevents clients from sending undeclared fields or extra array elements.
|
||||
@ -99,19 +110,17 @@ JSPG enforces a "Secure by Default" philosophy. All schemas are treated as if `u
|
||||
* **Ref Boundaries**: Strictness is reset when crossing `$ref` boundaries. The referenced schema's strictness is determined by its own definition (strict by default unless `extensible: true`), ignoring the caller's state.
|
||||
* **Inheritance**: Strictness is inherited. A schema extending a strict parent will also be strict unless it declares itself `extensible: true`. Conversely, a schema extending a loose parent will also be loose unless it declares itself `extensible: false`.
|
||||
|
||||
### 3. Implicit Keyword Shadowing
|
||||
Standard JSON Schema composition (`allOf`) is additive (Intersection), meaning constraints can only be tightened, not replaced. However, JSPG treats `$ref` differently when it appears alongside other properties to support object-oriented inheritance.
|
||||
|
||||
* **Inheritance (`$ref` + properties)**: When a schema uses `$ref` and defines its own properties, JSPG implements Smart Merge (or Shadowing). If a property is defined in the current schema, its constraints take precedence over the inherited constraints for that specific keyword.
|
||||
* **Example**: If Entity defines `type: { const: "entity" }` and Person (which refs Entity) defines `type: { const: "person" }`, validation passes for "person". The local const shadows the inherited const.
|
||||
* **Granularity**: Shadowing is per-keyword. If Entity defined `type: { const: "entity", minLength: 5 }`, Person would shadow `const` but still inherit `minLength: 5`.
|
||||
* **Composition (`allOf`)**: When using `allOf`, standard intersection rules apply. No shadowing occurs; all constraints from all branches must pass. This is used for mixins or interfaces.
|
||||
|
||||
### 4. Format Leniency for Empty Strings
|
||||
To simplify frontend form logic, the format validators for `uuid`, `date-time`, and `email` explicitly allow empty strings (`""`). This treats an empty string as "present but unset" rather than "invalid format".
|
||||
|
||||
### 5. Masking (Constructive Validation)
|
||||
JSPG supports a "Constructive Validation" mode via `mask_json_schema`. This is designed for high-performance API responses where the schema dictates the exact shape of the returned data.
|
||||
|
||||
* **Mechanism**: The validator traverses the instance against the schema.
|
||||
* **Valid Fields**: Kept in the output.
|
||||
* **Unknown/Extra Fields**: Silently removed (pruned) if `extensible: false` (default).
|
||||
* **Invalid Fields**: Still trigger standard validation errors.
|
||||
|
||||
This allows the database to return "raw" joined rows (e.g. `SELECT * FROM person JOIN organization ...`) and have JSPG automatically shape the result into the expected API response, removing any internal or unrelated columns not defined in the schema.
|
||||
|
||||
## 🏗️ Architecture
|
||||
|
||||
The extension is written in Rust using `pgrx` and structures its schema parser to mirror the Punc Generator's design:
|
||||
|
||||
Reference in New Issue
Block a user