@fougere/schema 0.2.0-alpha.1 → 0.3.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/EntityDeclarations.d.ts +22 -0
- package/dist/EntityDeclarations.d.ts.map +1 -0
- package/dist/EntityDeclarations.js +19 -0
- package/dist/EntityDeclarations.js.map +1 -0
- package/dist/Field.d.ts +21 -0
- package/dist/Field.d.ts.map +1 -0
- package/dist/Field.js +50 -0
- package/dist/Field.js.map +1 -0
- package/dist/Schema.d.ts +67 -0
- package/dist/Schema.d.ts.map +1 -0
- package/dist/Schema.js +152 -0
- package/dist/Schema.js.map +1 -0
- package/dist/SchemaView.d.ts +18 -0
- package/dist/SchemaView.d.ts.map +1 -0
- package/dist/SchemaView.js +2 -0
- package/dist/SchemaView.js.map +1 -0
- package/dist/axis/Axis.d.ts +10 -0
- package/dist/axis/Axis.d.ts.map +1 -0
- package/dist/axis/Axis.js +9 -0
- package/dist/axis/Axis.js.map +1 -0
- package/dist/axis/Meta.d.ts +4 -0
- package/dist/axis/Meta.d.ts.map +1 -0
- package/dist/axis/Meta.js +2 -0
- package/dist/axis/Meta.js.map +1 -0
- package/dist/axis/boundary/Boundaries.d.ts +20 -0
- package/dist/axis/boundary/Boundaries.d.ts.map +1 -0
- package/dist/axis/boundary/Boundaries.js +31 -0
- package/dist/axis/boundary/Boundaries.js.map +1 -0
- package/dist/axis/boundary/Boundary.d.ts +26 -0
- package/dist/axis/boundary/Boundary.d.ts.map +1 -0
- package/dist/axis/boundary/Boundary.js +53 -0
- package/dist/axis/boundary/Boundary.js.map +1 -0
- package/dist/axis/boundary/axis.d.ts +4 -0
- package/dist/axis/boundary/axis.d.ts.map +1 -0
- package/dist/axis/boundary/axis.js +29 -0
- package/dist/axis/boundary/axis.js.map +1 -0
- package/dist/axis/lifecycle/Generators.d.ts +4 -0
- package/dist/axis/lifecycle/Generators.d.ts.map +1 -0
- package/dist/axis/lifecycle/Generators.js +8 -0
- package/dist/axis/lifecycle/Generators.js.map +1 -0
- package/dist/axis/lifecycle/Lifecycle.d.ts +28 -0
- package/dist/axis/lifecycle/Lifecycle.d.ts.map +1 -0
- package/dist/axis/lifecycle/Lifecycle.js +38 -0
- package/dist/axis/lifecycle/Lifecycle.js.map +1 -0
- package/dist/axis/lifecycle/apply.d.ts +8 -0
- package/dist/axis/lifecycle/apply.d.ts.map +1 -0
- package/dist/axis/lifecycle/apply.js +72 -0
- package/dist/axis/lifecycle/apply.js.map +1 -0
- package/dist/axis/lifecycle/axis.d.ts +4 -0
- package/dist/axis/lifecycle/axis.d.ts.map +1 -0
- package/dist/axis/lifecycle/axis.js +46 -0
- package/dist/axis/lifecycle/axis.js.map +1 -0
- package/dist/axis/role/Relation.d.ts +10 -0
- package/dist/axis/role/Relation.d.ts.map +1 -0
- package/dist/axis/role/Relation.js +6 -0
- package/dist/axis/role/Relation.js.map +1 -0
- package/dist/axis/role/Role.d.ts +38 -0
- package/dist/axis/role/Role.d.ts.map +1 -0
- package/dist/axis/role/Role.js +50 -0
- package/dist/axis/role/Role.js.map +1 -0
- package/dist/axis/role/axis.d.ts +5 -0
- package/dist/axis/role/axis.d.ts.map +1 -0
- package/dist/axis/role/axis.js +101 -0
- package/dist/axis/role/axis.js.map +1 -0
- package/dist/axis/shape/Formats.d.ts +8 -0
- package/dist/axis/shape/Formats.d.ts.map +1 -0
- package/dist/axis/shape/Formats.js +10 -0
- package/dist/axis/shape/Formats.js.map +1 -0
- package/dist/axis/shape/Shape.d.ts +58 -0
- package/dist/axis/shape/Shape.d.ts.map +1 -0
- package/dist/axis/shape/Shape.js +52 -0
- package/dist/axis/shape/Shape.js.map +1 -0
- package/dist/card/Descriptor.d.ts +55 -0
- package/dist/card/Descriptor.d.ts.map +1 -0
- package/dist/card/Descriptor.js +2 -0
- package/dist/card/Descriptor.js.map +1 -0
- package/dist/card/describe.d.ts +12 -0
- package/dist/card/describe.d.ts.map +1 -0
- package/dist/card/describe.js +83 -0
- package/dist/card/describe.js.map +1 -0
- package/dist/card/diff.d.ts +140 -0
- package/dist/card/diff.d.ts.map +1 -0
- package/dist/card/diff.js +123 -0
- package/dist/card/diff.js.map +1 -0
- package/dist/card/reconstruct.d.ts +11 -0
- package/dist/card/reconstruct.d.ts.map +1 -0
- package/dist/card/reconstruct.js +83 -0
- package/dist/card/reconstruct.js.map +1 -0
- package/dist/card/typescript.d.ts +17 -0
- package/dist/card/typescript.d.ts.map +1 -0
- package/dist/card/typescript.js +93 -0
- package/dist/card/typescript.js.map +1 -0
- package/dist/clean.d.ts +2 -0
- package/dist/clean.d.ts.map +1 -0
- package/dist/{projections/card.js → clean.js} +1 -2
- package/dist/clean.js.map +1 -0
- package/dist/constraint/FieldGroup.d.ts +21 -0
- package/dist/constraint/FieldGroup.d.ts.map +1 -0
- package/dist/constraint/FieldGroup.js +65 -0
- package/dist/constraint/FieldGroup.js.map +1 -0
- package/dist/constraint/Unique.d.ts +4 -0
- package/dist/constraint/Unique.d.ts.map +1 -0
- package/dist/constraint/Unique.js +4 -0
- package/dist/constraint/Unique.js.map +1 -0
- package/dist/entity.d.ts +4 -116
- package/dist/entity.d.ts.map +1 -1
- package/dist/entity.js +13 -289
- package/dist/entity.js.map +1 -1
- package/dist/index.d.ts +35 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -26
- package/dist/index.js.map +1 -1
- package/dist/judge/Judge.d.ts +14 -0
- package/dist/judge/Judge.d.ts.map +1 -0
- package/dist/judge/Judge.js +161 -0
- package/dist/judge/Judge.js.map +1 -0
- package/dist/judge/cases.d.ts +50 -0
- package/dist/judge/cases.d.ts.map +1 -0
- package/dist/judge/cases.js +100 -0
- package/dist/judge/cases.js.map +1 -0
- package/dist/judge/form.d.ts +11 -0
- package/dist/judge/form.d.ts.map +1 -0
- package/dist/judge/form.js +18 -0
- package/dist/judge/form.js.map +1 -0
- package/dist/judge/options.d.ts +4 -0
- package/dist/judge/options.d.ts.map +1 -0
- package/dist/judge/options.js +2 -0
- package/dist/judge/options.js.map +1 -0
- package/dist/judge/result.d.ts +17 -0
- package/dist/judge/result.d.ts.map +1 -0
- package/dist/judge/result.js +2 -0
- package/dist/judge/result.js.map +1 -0
- package/dist/name.d.ts +1 -14
- package/dist/name.d.ts.map +1 -1
- package/dist/name.js +3 -14
- package/dist/name.js.map +1 -1
- package/dist/projection/encode.d.ts +3 -0
- package/dist/projection/encode.d.ts.map +1 -0
- package/dist/projection/encode.js +19 -0
- package/dist/projection/encode.js.map +1 -0
- package/dist/projection/io.d.ts +4 -0
- package/dist/projection/io.d.ts.map +1 -0
- package/dist/projection/io.js +29 -0
- package/dist/projection/io.js.map +1 -0
- package/dist/projection/source.d.ts +7 -0
- package/dist/projection/source.d.ts.map +1 -0
- package/dist/projection/source.js +11 -0
- package/dist/projection/source.js.map +1 -0
- package/dist/{projections → projection}/standard.d.ts +0 -8
- package/dist/projection/standard.d.ts.map +1 -0
- package/dist/projection/standard.js +2 -0
- package/dist/projection/standard.js.map +1 -0
- package/dist/same.d.ts +6 -0
- package/dist/same.d.ts.map +1 -0
- package/dist/same.js +35 -0
- package/dist/same.js.map +1 -0
- package/dist/vocabulary/bool.d.ts +1 -1
- package/dist/vocabulary/bool.d.ts.map +1 -1
- package/dist/vocabulary/bool.js +2 -2
- package/dist/vocabulary/bool.js.map +1 -1
- package/dist/vocabulary/created.d.ts +2 -12
- package/dist/vocabulary/created.d.ts.map +1 -1
- package/dist/vocabulary/created.js +3 -14
- package/dist/vocabulary/created.js.map +1 -1
- package/dist/vocabulary/date.d.ts +1 -1
- package/dist/vocabulary/date.d.ts.map +1 -1
- package/dist/vocabulary/date.js +2 -2
- package/dist/vocabulary/date.js.map +1 -1
- package/dist/vocabulary/email.d.ts +1 -2
- package/dist/vocabulary/email.d.ts.map +1 -1
- package/dist/vocabulary/email.js +0 -1
- package/dist/vocabulary/email.js.map +1 -1
- package/dist/vocabulary/immutable.d.ts +2 -8
- package/dist/vocabulary/immutable.d.ts.map +1 -1
- package/dist/vocabulary/immutable.js +4 -10
- package/dist/vocabulary/immutable.js.map +1 -1
- package/dist/vocabulary/indexed.d.ts +2 -18
- package/dist/vocabulary/indexed.d.ts.map +1 -1
- package/dist/vocabulary/indexed.js +2 -20
- package/dist/vocabulary/indexed.js.map +1 -1
- package/dist/vocabulary/json.d.ts +3 -16
- package/dist/vocabulary/json.d.ts.map +1 -1
- package/dist/vocabulary/json.js +12 -9
- package/dist/vocabulary/json.js.map +1 -1
- package/dist/vocabulary/list.d.ts +2 -16
- package/dist/vocabulary/list.d.ts.map +1 -1
- package/dist/vocabulary/list.js +3 -15
- package/dist/vocabulary/list.js.map +1 -1
- package/dist/vocabulary/many.d.ts +2 -6
- package/dist/vocabulary/many.d.ts.map +1 -1
- package/dist/vocabulary/many.js +4 -7
- package/dist/vocabulary/many.js.map +1 -1
- package/dist/vocabulary/nullable.d.ts +2 -8
- package/dist/vocabulary/nullable.d.ts.map +1 -1
- package/dist/vocabulary/nullable.js +5 -12
- package/dist/vocabulary/nullable.js.map +1 -1
- package/dist/vocabulary/number.d.ts +1 -1
- package/dist/vocabulary/number.d.ts.map +1 -1
- package/dist/vocabulary/number.js +2 -2
- package/dist/vocabulary/number.js.map +1 -1
- package/dist/vocabulary/oneOf.d.ts +1 -8
- package/dist/vocabulary/oneOf.d.ts.map +1 -1
- package/dist/vocabulary/oneOf.js +2 -2
- package/dist/vocabulary/oneOf.js.map +1 -1
- package/dist/vocabulary/optional.d.ts +2 -9
- package/dist/vocabulary/optional.d.ts.map +1 -1
- package/dist/vocabulary/optional.js +6 -14
- package/dist/vocabulary/optional.js.map +1 -1
- package/dist/vocabulary/primary.d.ts +4 -17
- package/dist/vocabulary/primary.d.ts.map +1 -1
- package/dist/vocabulary/primary.js +10 -13
- package/dist/vocabulary/primary.js.map +1 -1
- package/dist/vocabulary/readOnly.d.ts +2 -9
- package/dist/vocabulary/readOnly.d.ts.map +1 -1
- package/dist/vocabulary/readOnly.js +5 -11
- package/dist/vocabulary/readOnly.js.map +1 -1
- package/dist/vocabulary/ref.d.ts +2 -7
- package/dist/vocabulary/ref.d.ts.map +1 -1
- package/dist/vocabulary/ref.js +3 -8
- package/dist/vocabulary/ref.js.map +1 -1
- package/dist/vocabulary/text.d.ts +2 -2
- package/dist/vocabulary/text.d.ts.map +1 -1
- package/dist/vocabulary/text.js +2 -2
- package/dist/vocabulary/text.js.map +1 -1
- package/dist/vocabulary/unique.d.ts +2 -22
- package/dist/vocabulary/unique.d.ts.map +1 -1
- package/dist/vocabulary/unique.js +5 -24
- package/dist/vocabulary/unique.js.map +1 -1
- package/dist/vocabulary/updated.d.ts +2 -7
- package/dist/vocabulary/updated.d.ts.map +1 -1
- package/dist/vocabulary/updated.js +1 -7
- package/dist/vocabulary/updated.js.map +1 -1
- package/dist/vocabulary/url.d.ts +1 -2
- package/dist/vocabulary/url.d.ts.map +1 -1
- package/dist/vocabulary/url.js +0 -1
- package/dist/vocabulary/url.js.map +1 -1
- package/dist/vocabulary/vocabulary.d.ts +5 -0
- package/dist/vocabulary/vocabulary.d.ts.map +1 -0
- package/dist/vocabulary/vocabulary.js +31 -0
- package/dist/vocabulary/vocabulary.js.map +1 -0
- package/dist/vocabulary/writeOnly.d.ts +2 -8
- package/dist/vocabulary/writeOnly.d.ts.map +1 -1
- package/dist/vocabulary/writeOnly.js +5 -10
- package/dist/vocabulary/writeOnly.js.map +1 -1
- package/package.json +2 -2
- package/dist/field/boundary.d.ts +0 -86
- package/dist/field/boundary.d.ts.map +0 -1
- package/dist/field/boundary.js +0 -94
- package/dist/field/boundary.js.map +0 -1
- package/dist/field/field.d.ts +0 -64
- package/dist/field/field.d.ts.map +0 -1
- package/dist/field/field.js +0 -28
- package/dist/field/field.js.map +0 -1
- package/dist/field/index.d.ts +0 -7
- package/dist/field/index.d.ts.map +0 -1
- package/dist/field/index.js +0 -9
- package/dist/field/index.js.map +0 -1
- package/dist/field/lifecycle.d.ts +0 -40
- package/dist/field/lifecycle.d.ts.map +0 -1
- package/dist/field/lifecycle.js +0 -27
- package/dist/field/lifecycle.js.map +0 -1
- package/dist/field/meta.d.ts +0 -9
- package/dist/field/meta.d.ts.map +0 -1
- package/dist/field/meta.js +0 -7
- package/dist/field/meta.js.map +0 -1
- package/dist/field/role.d.ts +0 -54
- package/dist/field/role.d.ts.map +0 -1
- package/dist/field/role.js +0 -55
- package/dist/field/role.js.map +0 -1
- package/dist/field/shape.d.ts +0 -77
- package/dist/field/shape.d.ts.map +0 -1
- package/dist/field/shape.js +0 -49
- package/dist/field/shape.js.map +0 -1
- package/dist/hints.d.ts +0 -33
- package/dist/hints.d.ts.map +0 -1
- package/dist/hints.js +0 -2
- package/dist/hints.js.map +0 -1
- package/dist/projections/card.d.ts +0 -117
- package/dist/projections/card.d.ts.map +0 -1
- package/dist/projections/card.js.map +0 -1
- package/dist/projections/describe.d.ts +0 -25
- package/dist/projections/describe.d.ts.map +0 -1
- package/dist/projections/describe.js +0 -136
- package/dist/projections/describe.js.map +0 -1
- package/dist/projections/encode.d.ts +0 -11
- package/dist/projections/encode.d.ts.map +0 -1
- package/dist/projections/encode.js +0 -26
- package/dist/projections/encode.js.map +0 -1
- package/dist/projections/io.d.ts +0 -30
- package/dist/projections/io.d.ts.map +0 -1
- package/dist/projections/io.js +0 -52
- package/dist/projections/io.js.map +0 -1
- package/dist/projections/lifecycle.d.ts +0 -43
- package/dist/projections/lifecycle.d.ts.map +0 -1
- package/dist/projections/lifecycle.js +0 -115
- package/dist/projections/lifecycle.js.map +0 -1
- package/dist/projections/reconstruct.d.ts +0 -36
- package/dist/projections/reconstruct.d.ts.map +0 -1
- package/dist/projections/reconstruct.js +0 -149
- package/dist/projections/reconstruct.js.map +0 -1
- package/dist/projections/source.d.ts +0 -41
- package/dist/projections/source.d.ts.map +0 -1
- package/dist/projections/source.js +0 -37
- package/dist/projections/source.js.map +0 -1
- package/dist/projections/standard.d.ts.map +0 -1
- package/dist/projections/standard.js +0 -10
- package/dist/projections/standard.js.map +0 -1
- package/dist/projections/typescript.d.ts +0 -53
- package/dist/projections/typescript.d.ts.map +0 -1
- package/dist/projections/typescript.js +0 -184
- package/dist/projections/typescript.js.map +0 -1
- package/dist/projections/validation.d.ts +0 -57
- package/dist/projections/validation.d.ts.map +0 -1
- package/dist/projections/validation.js +0 -148
- package/dist/projections/validation.js.map +0 -1
- package/dist/unique.d.ts +0 -65
- package/dist/unique.d.ts.map +0 -1
- package/dist/unique.js +0 -80
- package/dist/unique.js.map +0 -1
package/dist/hints.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hints.d.ts","sourceRoot":"","sources":["../src/hints.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,WAAW,YAAY,CAAC,CAAC,SAAS,MAAM;CAAI;AAElD;;;;GAIG;AACH,MAAM,MAAM,KAAK,CAAC,OAAO,SAAS,MAAM,IAAI;KACzC,CAAC,IAAI,MAAM,YAAY,CAAC,OAAO,CAAC,MAAM,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,YAAY,CACtE,OAAO,CAAC,MAAM,OAAO,EAAE,MAAM,CAAC,CAC/B,CAAC,CAAC,CAAC;CACL,CAAC"}
|
package/dist/hints.js
DELETED
package/dist/hints.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hints.js","sourceRoot":"","sources":["../src/hints.ts"],"names":[],"mappings":""}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import type { BoundaryRef, Lifecycle } from '../field/index.js';
|
|
2
|
-
/**
|
|
3
|
-
* The portable schema descriptor — Fougère's "carte d'identité" of an entity.
|
|
4
|
-
*
|
|
5
|
-
* It is a JSON Schema document (the `shape` axis, a near-identity projection now
|
|
6
|
-
* that {@link FieldDescriptor} already speaks JSON Schema) carrying the three axes JSON
|
|
7
|
-
* Schema cannot express under a single `x-fougere` extension key per field: `role`
|
|
8
|
-
* (identity/relations), `lifecycle` (write rules), `boundary` (wire↔domain).
|
|
9
|
-
* The normal forms (named tokens, pure JSON) ARE the wire form — lifecycle and
|
|
10
|
-
* boundary travel verbatim, no descriptor twin to maintain.
|
|
11
|
-
*
|
|
12
|
-
* It is the structural counterpart of Standard Schema (`~standard`): where
|
|
13
|
-
* `~standard` is the live, opaque "I can validate" interface, the descriptor is the
|
|
14
|
-
* transparent, serialisable description that crosses the wire. `describe()` produces
|
|
15
|
-
* it; a single `reconstruct()` reads it back into a working schema (validate + from).
|
|
16
|
-
*
|
|
17
|
-
* Honest losses vs the live field map — a reference becomes a name, a live function
|
|
18
|
-
* does not travel:
|
|
19
|
-
* - `role.relation.to` (a `() => Entity` thunk) → {@link RelationDescriptor.to}, a name.
|
|
20
|
-
* - a custom generator or boundary decoder/encoder travels by NAME, re-resolved
|
|
21
|
-
* against the consumer's registry ("unknown generator/alias" is loud and local).
|
|
22
|
-
*/
|
|
23
|
-
/** JSON Schema's `type` values, plus `null` for the nullable union form. */
|
|
24
|
-
export type JsonSchemaType = 'string' | 'number' | 'integer' | 'boolean' | 'object' | 'array' | 'null';
|
|
25
|
-
/**
|
|
26
|
-
* One field as a JSON Schema property. The shape keywords sit at the top level
|
|
27
|
-
* (so an external JSON Schema tool reads them directly); the Fougère-only axes
|
|
28
|
-
* live under `x-fougere`. A nullable field folds into the union `type: ['T','null']`.
|
|
29
|
-
*/
|
|
30
|
-
export interface FieldDescriptor {
|
|
31
|
-
type?: JsonSchemaType | JsonSchemaType[];
|
|
32
|
-
/** JSON Schema format predicate (`date-time` for a date value, email, uuid, uri…). */
|
|
33
|
-
format?: 'date-time' | 'date' | 'time' | 'email' | 'uuid' | 'uri';
|
|
34
|
-
enum?: readonly (string | null)[];
|
|
35
|
-
minLength?: number;
|
|
36
|
-
maxLength?: number;
|
|
37
|
-
pattern?: string;
|
|
38
|
-
minimum?: number;
|
|
39
|
-
maximum?: number;
|
|
40
|
-
/** A value list's element shape (`list(text())`). A bare `many` relation has none. */
|
|
41
|
-
items?: FieldDescriptor;
|
|
42
|
-
minItems?: number;
|
|
43
|
-
maxItems?: number;
|
|
44
|
-
/** An embedded value object (`json(Entity)`): the nested shape-only properties. */
|
|
45
|
-
properties?: Record<string, FieldDescriptor>;
|
|
46
|
-
/** The embedded object's required keys (JSON Schema nesting, travels verbatim). */
|
|
47
|
-
required?: readonly string[];
|
|
48
|
-
/** JSON Schema standard keyword — mirrors the field's `description`. */
|
|
49
|
-
description?: string;
|
|
50
|
-
'x-fougere'?: FieldExtension;
|
|
51
|
-
}
|
|
52
|
-
/** The three axes JSON Schema can't express, namespaced under a field's `x-fougere`. */
|
|
53
|
-
export interface FieldExtension {
|
|
54
|
-
role?: RoleDescriptor;
|
|
55
|
-
/** The normal form travels verbatim — named tokens, pure JSON. */
|
|
56
|
-
lifecycle?: Lifecycle;
|
|
57
|
-
boundary?: BoundaryRef;
|
|
58
|
-
}
|
|
59
|
-
export interface RoleDescriptor {
|
|
60
|
-
primary?: boolean;
|
|
61
|
-
/**
|
|
62
|
-
* The unique constraints this field is a member of — one member list each, member
|
|
63
|
-
* names spelled out. `[["slug"]]` is unique on its own; `[["listId","docId"]]` on both
|
|
64
|
-
* members says the pair is unique together, which is the fact a foreign consumer could
|
|
65
|
-
* not read before: the card carried one boolean per field and the pair vanished.
|
|
66
|
-
*
|
|
67
|
-
* Self-reference is resolved on the way out (a lone `unique()` holds `[]` in memory,
|
|
68
|
-
* because a field does not know its own key) — a card always names its members, so a
|
|
69
|
-
* reader never has to know which field a group hangs on.
|
|
70
|
-
*/
|
|
71
|
-
unique?: string[][];
|
|
72
|
-
index?: boolean;
|
|
73
|
-
relation?: RelationDescriptor;
|
|
74
|
-
}
|
|
75
|
-
/** A relation as portable data: the target is a NAME, not a live entity thunk. */
|
|
76
|
-
export interface RelationDescriptor {
|
|
77
|
-
to: string;
|
|
78
|
-
kind: 'one' | 'many';
|
|
79
|
-
onDelete?: 'cascade' | 'restrict' | 'set null';
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* A whole entity as a JSON Schema object document. `version`/`vendor` mirror Standard
|
|
83
|
-
* Schema's own (versioned + vendored), under `x-` extension keywords so the document
|
|
84
|
-
* stays a valid JSON Schema for external tooling.
|
|
85
|
-
*/
|
|
86
|
-
export interface SchemaDescriptor {
|
|
87
|
-
/** The entity name. */
|
|
88
|
-
title?: string;
|
|
89
|
-
type: 'object';
|
|
90
|
-
properties: Record<string, FieldDescriptor>;
|
|
91
|
-
/**
|
|
92
|
-
* Create-time semantics: the names a caller MUST supply — no `lifecycle.create`
|
|
93
|
-
* rule answers absence (nor a `many` relation, absent → `[]`). Presence only:
|
|
94
|
-
* a nullable-but-required field IS listed (supply `null`, don't omit the key).
|
|
95
|
-
* Narrower than JSON Schema's context-free `required`: it answers "what must a
|
|
96
|
-
* creation payload carry?", not "what is always present when read?".
|
|
97
|
-
*/
|
|
98
|
-
required?: string[];
|
|
99
|
-
'x-fougere-version': 1;
|
|
100
|
-
'x-fougere-vendor': 'fougere';
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* A self-contained set of entity cards — a JSON Schema bundle. Every entity lives
|
|
104
|
-
* under `$defs`, keyed by name; a relation's `to` is that name — the `$ref` into
|
|
105
|
-
* `$defs`. The document is portable AND circular-safe: a relation is a pointer, never
|
|
106
|
-
* an inlined sub-schema, so `Post → Author → Post` is two references, not infinite
|
|
107
|
-
* nesting. `reconstructSet` resolves those `$ref`s so a synced relation hands back the
|
|
108
|
-
* real reconstructed target (it feeds adapters), not just a name.
|
|
109
|
-
*/
|
|
110
|
-
export interface SchemaBundle {
|
|
111
|
-
$defs: Record<string, SchemaDescriptor>;
|
|
112
|
-
'x-fougere-version': 1;
|
|
113
|
-
'x-fougere-vendor': 'fougere';
|
|
114
|
-
}
|
|
115
|
-
/** Drop keys whose value is `undefined`, so the descriptor stays clean JSON. */
|
|
116
|
-
export declare function clean<T extends Record<string, unknown>>(obj: T): T;
|
|
117
|
-
//# sourceMappingURL=card.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"card.d.ts","sourceRoot":"","sources":["../../src/projections/card.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEhE;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,4EAA4E;AAC5E,MAAM,MAAM,cAAc,GACtB,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,SAAS,GACT,QAAQ,GACR,OAAO,GACP,MAAM,CAAC;AAEX;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,cAAc,GAAG,cAAc,EAAE,CAAC;IACzC,sFAAsF;IACtF,MAAM,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;IAClE,IAAI,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sFAAsF;IACtF,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC7C,mFAAmF;IACnF,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,cAAc,CAAC;CAC9B;AAED,wFAAwF;AACxF,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,kEAAkE;IAClE,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,QAAQ,CAAC,EAAE,WAAW,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC/B;AAED,kFAAkF;AAClF,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,KAAK,GAAG,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;CAChD;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,uBAAuB;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,QAAQ,CAAC;IACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC5C;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,mBAAmB,EAAE,CAAC,CAAC;IACvB,kBAAkB,EAAE,SAAS,CAAC;CAC/B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACxC,mBAAmB,EAAE,CAAC,CAAC;IACvB,kBAAkB,EAAE,SAAS,CAAC;CAC/B;AAED,gFAAgF;AAChF,wBAAgB,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAGlE"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"card.js","sourceRoot":"","sources":["../../src/projections/card.ts"],"names":[],"mappings":"AAkIA,gFAAgF;AAChF,MAAM,UAAU,KAAK,CAAoC,GAAM;IAC7D,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,SAAS;YAAE,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;IAChF,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { SchemaLike } from '../field/index.js';
|
|
2
|
-
import { type SchemaBundle, type SchemaDescriptor } from './card.js';
|
|
3
|
-
/**
|
|
4
|
-
* Produce the portable card for a schema — THE single canonical serialiser
|
|
5
|
-
* (the hand-rolled copies in adapters call this). `name` titles the entity; it
|
|
6
|
-
* falls back to the schema's source/class name when omitted.
|
|
7
|
-
*/
|
|
8
|
-
export declare function describe(schema: SchemaLike, name?: string): SchemaDescriptor;
|
|
9
|
-
/**
|
|
10
|
-
* The name of the schema a view came from — `Post` for `Post`, and `Post` for
|
|
11
|
-
* `Post.pick('title')` too, since a derivation carries its `source`.
|
|
12
|
-
*
|
|
13
|
-
* Exported because two projections need the SAME answer: the card titles an entity with it,
|
|
14
|
-
* and GraphQL names a field's enum type with it — an enum named after the view would give
|
|
15
|
-
* `Post.status` and `CreatePostInput.status` two incompatible types for one set of values.
|
|
16
|
-
*/
|
|
17
|
-
export declare function sourceNameOf(schema: SchemaLike): string | undefined;
|
|
18
|
-
/**
|
|
19
|
-
* Describe a whole set of entities as one self-contained bundle (the `$defs`
|
|
20
|
-
* document). Accepts a name→schema record or an array (names taken from the
|
|
21
|
-
* schemas). Keys carry the registration key, the same spelling `describe` gives a
|
|
22
|
-
* relation's `to`, so `$ref`-by-name resolves cleanly in {@link reconstructSet}.
|
|
23
|
-
*/
|
|
24
|
-
export declare function describeSet(schemas: Record<string, SchemaLike> | SchemaLike[]): SchemaBundle;
|
|
25
|
-
//# sourceMappingURL=describe.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"describe.d.ts","sourceRoot":"","sources":["../../src/projections/describe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAGpE,OAAO,EAML,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACtB,MAAM,WAAW,CAAC;AAuEnB;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAiB5E;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,SAAS,CAGnE;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,UAAU,EAAE,GAAG,YAAY,CAU5F"}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { uniqueMembers, boundaryOf } from '../field/index.js';
|
|
2
|
-
import { registrationKeyOf } from '../name.js';
|
|
3
|
-
import { clean, } from './card.js';
|
|
4
|
-
// ─── describe — schema → card (the single canonical serialiser) ────
|
|
5
|
-
/**
|
|
6
|
-
* A caller must supply this field: no create rule answers absence, and it is not
|
|
7
|
-
* a `many` relation (absent → `[]`). Presence only — nullability is the OTHER
|
|
8
|
-
* axis: a `nullable()` field stays required (the caller may supply `null`, not
|
|
9
|
-
* omit the key), exactly what `validateFields` enforces.
|
|
10
|
-
*/
|
|
11
|
-
function isRequired(field) {
|
|
12
|
-
// Read `boundary` for the same reason `validateFields` does: a server-owned
|
|
13
|
-
// field is one a caller may never supply, so listing it as required states a
|
|
14
|
-
// demand the door then refuses with `Read-only`. Same stance as OpenAPI's
|
|
15
|
-
// readOnly+required, and the two readers now answer from the same axis.
|
|
16
|
-
if (boundaryOf(field).in === 'closed')
|
|
17
|
-
return false;
|
|
18
|
-
if (field.lifecycle?.create !== undefined)
|
|
19
|
-
return false;
|
|
20
|
-
if (field.role?.relation?.kind === 'many')
|
|
21
|
-
return false;
|
|
22
|
-
return true;
|
|
23
|
-
}
|
|
24
|
-
function describeRole(role, key) {
|
|
25
|
-
const out = {};
|
|
26
|
-
if (role.primary)
|
|
27
|
-
out.primary = true;
|
|
28
|
-
// Members are spelled out on the wire: in memory a lone `unique()` holds the empty
|
|
29
|
-
// self-reference (a field cannot name its own key), and a consumer has no way to know
|
|
30
|
-
// which field a group hangs on. `key` is that missing half, so it is resolved here.
|
|
31
|
-
if (role.unique?.length)
|
|
32
|
-
out.unique = role.unique.map((group) => uniqueMembers(group, key));
|
|
33
|
-
if (role.index)
|
|
34
|
-
out.index = true;
|
|
35
|
-
if (role.relation) {
|
|
36
|
-
out.relation = clean({
|
|
37
|
-
to: registrationKeyOf(role.relation.to().name ?? ''), // thunk → name
|
|
38
|
-
kind: role.relation.kind,
|
|
39
|
-
onDelete: role.relation.onDelete,
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
return Object.keys(out).length ? out : undefined;
|
|
43
|
-
}
|
|
44
|
-
function describeExtension(field, key) {
|
|
45
|
-
const ext = {};
|
|
46
|
-
if (field.role)
|
|
47
|
-
ext.role = describeRole(field.role, key);
|
|
48
|
-
// The normal forms are named tokens, pure JSON — they travel verbatim
|
|
49
|
-
// (a custom generator travels by NAME, re-resolved against the consumer's registry).
|
|
50
|
-
if (field.lifecycle)
|
|
51
|
-
ext.lifecycle = field.lifecycle;
|
|
52
|
-
// Carry only an explicit override; the derived default (date → isoDate) is
|
|
53
|
-
// re-derived from `shape.format` on reconstruction (convention over config).
|
|
54
|
-
if (field.boundary !== undefined)
|
|
55
|
-
ext.boundary = field.boundary;
|
|
56
|
-
clean(ext);
|
|
57
|
-
return Object.keys(ext).length ? ext : undefined;
|
|
58
|
-
}
|
|
59
|
-
function describeField(field, key) {
|
|
60
|
-
const out = {};
|
|
61
|
-
// Shape is already JSON Schema's vocabulary — nullability included, as the
|
|
62
|
-
// `[T,'null']` type union — so its keywords copy verbatim (an embedded object's
|
|
63
|
-
// `properties`/`required` too, themselves shape-only).
|
|
64
|
-
if (field.shape) {
|
|
65
|
-
for (const [key, value] of Object.entries(field.shape)) {
|
|
66
|
-
if (value === undefined)
|
|
67
|
-
continue;
|
|
68
|
-
out[key] = value;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
else if (field.role?.relation?.kind === 'many') {
|
|
72
|
-
out.type = 'array';
|
|
73
|
-
}
|
|
74
|
-
if (field.meta?.description)
|
|
75
|
-
out.description = field.meta.description;
|
|
76
|
-
const ext = describeExtension(field, key);
|
|
77
|
-
if (ext)
|
|
78
|
-
out['x-fougere'] = ext;
|
|
79
|
-
return out;
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Produce the portable card for a schema — THE single canonical serialiser
|
|
83
|
-
* (the hand-rolled copies in adapters call this). `name` titles the entity; it
|
|
84
|
-
* falls back to the schema's source/class name when omitted.
|
|
85
|
-
*/
|
|
86
|
-
export function describe(schema, name) {
|
|
87
|
-
const properties = {};
|
|
88
|
-
const required = [];
|
|
89
|
-
for (const [key, field] of Object.entries(schema.getFields())) {
|
|
90
|
-
properties[key] = describeField(field, key);
|
|
91
|
-
if (isRequired(field))
|
|
92
|
-
required.push(key);
|
|
93
|
-
}
|
|
94
|
-
const descriptor = {
|
|
95
|
-
type: 'object',
|
|
96
|
-
properties,
|
|
97
|
-
'x-fougere-version': 1,
|
|
98
|
-
'x-fougere-vendor': 'fougere',
|
|
99
|
-
};
|
|
100
|
-
const title = name ?? sourceNameOf(schema);
|
|
101
|
-
if (title)
|
|
102
|
-
descriptor.title = title;
|
|
103
|
-
if (required.length)
|
|
104
|
-
descriptor.required = required;
|
|
105
|
-
return descriptor;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* The name of the schema a view came from — `Post` for `Post`, and `Post` for
|
|
109
|
-
* `Post.pick('title')` too, since a derivation carries its `source`.
|
|
110
|
-
*
|
|
111
|
-
* Exported because two projections need the SAME answer: the card titles an entity with it,
|
|
112
|
-
* and GraphQL names a field's enum type with it — an enum named after the view would give
|
|
113
|
-
* `Post.status` and `CreatePostInput.status` two incompatible types for one set of values.
|
|
114
|
-
*/
|
|
115
|
-
export function sourceNameOf(schema) {
|
|
116
|
-
const s = schema;
|
|
117
|
-
return s.source?.name ?? s.name;
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Describe a whole set of entities as one self-contained bundle (the `$defs`
|
|
121
|
-
* document). Accepts a name→schema record or an array (names taken from the
|
|
122
|
-
* schemas). Keys carry the registration key, the same spelling `describe` gives a
|
|
123
|
-
* relation's `to`, so `$ref`-by-name resolves cleanly in {@link reconstructSet}.
|
|
124
|
-
*/
|
|
125
|
-
export function describeSet(schemas) {
|
|
126
|
-
const entries = Array.isArray(schemas)
|
|
127
|
-
? schemas.map((s) => [sourceNameOf(s) ?? '', s])
|
|
128
|
-
: Object.entries(schemas);
|
|
129
|
-
const $defs = {};
|
|
130
|
-
for (const [name, schema] of entries) {
|
|
131
|
-
const key = registrationKeyOf(name);
|
|
132
|
-
$defs[key] = describe(schema, key);
|
|
133
|
-
}
|
|
134
|
-
return { $defs, 'x-fougere-version': 1, 'x-fougere-vendor': 'fougere' };
|
|
135
|
-
}
|
|
136
|
-
//# sourceMappingURL=describe.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"describe.js","sourceRoot":"","sources":["../../src/projections/describe.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { Fields } from '../field/index.js';
|
|
2
|
-
/**
|
|
3
|
-
* Egress: encode a domain record into its wire form, field by field — the dual of
|
|
4
|
-
* `validateFields` (which decodes on ingress). A write-only field (boundary
|
|
5
|
-
* `out: 'closed'`, e.g. a password) is OMITTED — it never crosses outbound.
|
|
6
|
-
* Keys absent from `fields` (e.g. presenter computed fields) pass through
|
|
7
|
-
* untouched; null/undefined are left as-is.
|
|
8
|
-
* Shallow by design: a relation's nested rows are not deep-encoded here.
|
|
9
|
-
*/
|
|
10
|
-
export declare function encodeFields(fields: Fields, record: Record<string, unknown>): Record<string, unknown>;
|
|
11
|
-
//# sourceMappingURL=encode.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"encode.d.ts","sourceRoot":"","sources":["../../src/projections/encode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAGhD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAUrG"}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { boundaryOf, resolveBoundary } from '../field/index.js';
|
|
2
|
-
/**
|
|
3
|
-
* Egress: encode a domain record into its wire form, field by field — the dual of
|
|
4
|
-
* `validateFields` (which decodes on ingress). A write-only field (boundary
|
|
5
|
-
* `out: 'closed'`, e.g. a password) is OMITTED — it never crosses outbound.
|
|
6
|
-
* Keys absent from `fields` (e.g. presenter computed fields) pass through
|
|
7
|
-
* untouched; null/undefined are left as-is.
|
|
8
|
-
* Shallow by design: a relation's nested rows are not deep-encoded here.
|
|
9
|
-
*/
|
|
10
|
-
export function encodeFields(fields, record) {
|
|
11
|
-
const out = { ...record };
|
|
12
|
-
for (const [key, field] of Object.entries(fields)) {
|
|
13
|
-
if (!(key in record))
|
|
14
|
-
continue;
|
|
15
|
-
if (boundaryOf(field).out === 'closed') {
|
|
16
|
-
delete out[key];
|
|
17
|
-
continue;
|
|
18
|
-
}
|
|
19
|
-
const value = record[key];
|
|
20
|
-
if (value === null || value === undefined)
|
|
21
|
-
continue;
|
|
22
|
-
out[key] = resolveBoundary(field).encode(value);
|
|
23
|
-
}
|
|
24
|
-
return out;
|
|
25
|
-
}
|
|
26
|
-
//# sourceMappingURL=encode.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"encode.js","sourceRoot":"","sources":["../../src/projections/encode.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEhE;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc,EAAE,MAA+B;IAC1E,MAAM,GAAG,GAA4B,EAAE,GAAG,MAAM,EAAE,CAAC;IACnD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IAAI,CAAC,CAAC,GAAG,IAAI,MAAM,CAAC;YAAE,SAAS;QAC/B,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;YAAC,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;YAAC,SAAS;QAAC,CAAC;QACtE,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1B,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QACpD,GAAG,CAAC,GAAG,CAAC,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/projections/io.d.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { Fields } from '../field/index.js';
|
|
2
|
-
/**
|
|
3
|
-
* The two dual projections of a field set onto a client surface — membership
|
|
4
|
-
* only, derived from the axes.
|
|
5
|
-
*
|
|
6
|
-
* The create/patch MODE never changes membership, it only changes omissibility, which
|
|
7
|
-
* the view (`partial()`) carries.
|
|
8
|
-
*
|
|
9
|
-
* ⚠️ The audience lives in this comment, not in the data — no axis says "client", so
|
|
10
|
-
* these two functions are the only place the word exists. Surfaces are SUPPOSED to read
|
|
11
|
-
* them instead of restating the rules, and several don't: the GraphQL public
|
|
12
|
-
* `registerInput`, the runtime judge and the CLI each answer "what may a client supply"
|
|
13
|
-
* with a different set. The judge is the most permissive — a client-forged primary key
|
|
14
|
-
* reaches the ORM. Reconciling them needs the audience to become data.
|
|
15
|
-
*/
|
|
16
|
-
/**
|
|
17
|
-
* Ingress: what a CLIENT may supply. Excluded —
|
|
18
|
-
* - `role.primary` → identity is system-owned
|
|
19
|
-
* - `lifecycle.create === 'now'` → managed timestamps are stamped, not supplied
|
|
20
|
-
* - `role.relation.kind 'many'` → the collection lives on the other side
|
|
21
|
-
* - `boundary.in === 'closed'` → read-only, never accepted from a client
|
|
22
|
-
*/
|
|
23
|
-
export declare function inputFields(fields: Fields): Fields;
|
|
24
|
-
/**
|
|
25
|
-
* Egress: what a CLIENT may read. Excluded —
|
|
26
|
-
* - `boundary.out === 'closed'` → write-only (password), never emitted
|
|
27
|
-
* (`encodeFields` also omits them at runtime; types and payloads agree.)
|
|
28
|
-
*/
|
|
29
|
-
export declare function outputFields(fields: Fields): Fields;
|
|
30
|
-
//# sourceMappingURL=io.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../../src/projections/io.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAGhD;;;;;;;;;;;;;GAaG;AAEH;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAUlD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOnD"}
|
package/dist/projections/io.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { boundaryOf } from '../field/index.js';
|
|
2
|
-
/**
|
|
3
|
-
* The two dual projections of a field set onto a client surface — membership
|
|
4
|
-
* only, derived from the axes.
|
|
5
|
-
*
|
|
6
|
-
* The create/patch MODE never changes membership, it only changes omissibility, which
|
|
7
|
-
* the view (`partial()`) carries.
|
|
8
|
-
*
|
|
9
|
-
* ⚠️ The audience lives in this comment, not in the data — no axis says "client", so
|
|
10
|
-
* these two functions are the only place the word exists. Surfaces are SUPPOSED to read
|
|
11
|
-
* them instead of restating the rules, and several don't: the GraphQL public
|
|
12
|
-
* `registerInput`, the runtime judge and the CLI each answer "what may a client supply"
|
|
13
|
-
* with a different set. The judge is the most permissive — a client-forged primary key
|
|
14
|
-
* reaches the ORM. Reconciling them needs the audience to become data.
|
|
15
|
-
*/
|
|
16
|
-
/**
|
|
17
|
-
* Ingress: what a CLIENT may supply. Excluded —
|
|
18
|
-
* - `role.primary` → identity is system-owned
|
|
19
|
-
* - `lifecycle.create === 'now'` → managed timestamps are stamped, not supplied
|
|
20
|
-
* - `role.relation.kind 'many'` → the collection lives on the other side
|
|
21
|
-
* - `boundary.in === 'closed'` → read-only, never accepted from a client
|
|
22
|
-
*/
|
|
23
|
-
export function inputFields(fields) {
|
|
24
|
-
const result = {};
|
|
25
|
-
for (const [name, field] of Object.entries(fields)) {
|
|
26
|
-
if (field.role?.primary)
|
|
27
|
-
continue;
|
|
28
|
-
if (field.lifecycle?.create === 'now')
|
|
29
|
-
continue;
|
|
30
|
-
if (field.role?.relation?.kind === 'many')
|
|
31
|
-
continue;
|
|
32
|
-
if (boundaryOf(field).in === 'closed')
|
|
33
|
-
continue;
|
|
34
|
-
result[name] = field;
|
|
35
|
-
}
|
|
36
|
-
return result;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Egress: what a CLIENT may read. Excluded —
|
|
40
|
-
* - `boundary.out === 'closed'` → write-only (password), never emitted
|
|
41
|
-
* (`encodeFields` also omits them at runtime; types and payloads agree.)
|
|
42
|
-
*/
|
|
43
|
-
export function outputFields(fields) {
|
|
44
|
-
const result = {};
|
|
45
|
-
for (const [name, field] of Object.entries(fields)) {
|
|
46
|
-
if (boundaryOf(field).out === 'closed')
|
|
47
|
-
continue;
|
|
48
|
-
result[name] = field;
|
|
49
|
-
}
|
|
50
|
-
return result;
|
|
51
|
-
}
|
|
52
|
-
//# sourceMappingURL=io.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"io.js","sourceRoot":"","sources":["../../src/projections/io.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C;;;;;;;;;;;;;GAaG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,MAAM,MAAM,GAAW,EAAE,CAAC;IAC1B,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,IAAI,KAAK,CAAC,IAAI,EAAE,OAAO;YAAE,SAAS;QAClC,IAAI,KAAK,CAAC,SAAS,EAAE,MAAM,KAAK,KAAK;YAAE,SAAS;QAChD,IAAI,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,KAAK,MAAM;YAAE,SAAS;QACpD,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,QAAQ;YAAE,SAAS;QAChD,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;IACvB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,MAAM,MAAM,GAAW,EAAE,CAAC;IAC1B,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,QAAQ;YAAE,SAAS;QACjD,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;IACvB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The lifecycle axis, realized — once, for every storage.
|
|
3
|
-
*
|
|
4
|
-
* `validation.ts` states the split: the judge answers "is this value legal?" and never
|
|
5
|
-
* fills a hole; filling it — stamp `'now'`, apply `{ value }`, call `{ generate }` — is
|
|
6
|
-
* the storage's role at the point of persistence. The split is right. What was missing
|
|
7
|
-
* is that the framework shipped the RULE and never its realization, so each storage
|
|
8
|
-
* rewrote it and they drifted:
|
|
9
|
-
*
|
|
10
|
-
* - `schema-sql` realized `{ generate }` and `'now'` in its ORM, and `{ value }` in its
|
|
11
|
-
* DDL — as a column `DEFAULT` the database fills. A mechanism only SQL has.
|
|
12
|
-
* - The Nuxt module's fallback ORM realized none of the three: its signature is
|
|
13
|
-
* `(_entity, _name)`, both ignored, so it forces the name `id` and a random uuid.
|
|
14
|
-
* - A third-party adapter (MongoDB, written outside the repo on 2026-08-08) found the
|
|
15
|
-
* gap the only way available: a test that expected `'draft'` and got `undefined`.
|
|
16
|
-
*
|
|
17
|
-
* Measured, one entity, `oneOf('draft','published',{ default:'draft' })`: SQLite answered
|
|
18
|
-
* `'draft'`, MongoDB answered `undefined`. Same declaration, same port, two answers.
|
|
19
|
-
*
|
|
20
|
-
* So the rule and its realization live together now, and a storage adapter calls this
|
|
21
|
-
* instead of re-deriving it. `update: 'forbidden'` is NOT here: refusing a value is a
|
|
22
|
-
* judgment, and the façade already does it (`validation.ts`, patch mode).
|
|
23
|
-
*/
|
|
24
|
-
import { type Fields } from '../field/index.js';
|
|
25
|
-
type Row = Record<string, unknown>;
|
|
26
|
-
/**
|
|
27
|
-
* Fill what the entity says the system writes at creation.
|
|
28
|
-
*
|
|
29
|
-
* A value the caller supplied is never touched, including `null` — presence is the
|
|
30
|
-
* test, not truthiness, so `archivedAt: null` stays null rather than being re-filled.
|
|
31
|
-
*
|
|
32
|
-
* `'now'` produces a `Date`, the value the field declares. The storage converts it if
|
|
33
|
-
* its driver needs something else (`schema-sql/src/values.ts` does exactly that) —
|
|
34
|
-
* which is the same direction every other value travels.
|
|
35
|
-
*/
|
|
36
|
-
export declare function applyCreate(fields: Fields, input: Row): Row;
|
|
37
|
-
/**
|
|
38
|
-
* Fill what the entity says the system writes at every update — `updated()`, and
|
|
39
|
-
* nothing else. A supplied value wins, same rule as create.
|
|
40
|
-
*/
|
|
41
|
-
export declare function applyUpdate(fields: Fields, patch: Row): Row;
|
|
42
|
-
export {};
|
|
43
|
-
//# sourceMappingURL=lifecycle.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../src/projections/lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAyC,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAGvF,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AA8BnC;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,GAAG,CAqB3D;AAgBD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,GAAG,CAS3D"}
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The lifecycle axis, realized — once, for every storage.
|
|
3
|
-
*
|
|
4
|
-
* `validation.ts` states the split: the judge answers "is this value legal?" and never
|
|
5
|
-
* fills a hole; filling it — stamp `'now'`, apply `{ value }`, call `{ generate }` — is
|
|
6
|
-
* the storage's role at the point of persistence. The split is right. What was missing
|
|
7
|
-
* is that the framework shipped the RULE and never its realization, so each storage
|
|
8
|
-
* rewrote it and they drifted:
|
|
9
|
-
*
|
|
10
|
-
* - `schema-sql` realized `{ generate }` and `'now'` in its ORM, and `{ value }` in its
|
|
11
|
-
* DDL — as a column `DEFAULT` the database fills. A mechanism only SQL has.
|
|
12
|
-
* - The Nuxt module's fallback ORM realized none of the three: its signature is
|
|
13
|
-
* `(_entity, _name)`, both ignored, so it forces the name `id` and a random uuid.
|
|
14
|
-
* - A third-party adapter (MongoDB, written outside the repo on 2026-08-08) found the
|
|
15
|
-
* gap the only way available: a test that expected `'draft'` and got `undefined`.
|
|
16
|
-
*
|
|
17
|
-
* Measured, one entity, `oneOf('draft','published',{ default:'draft' })`: SQLite answered
|
|
18
|
-
* `'draft'`, MongoDB answered `undefined`. Same declaration, same port, two answers.
|
|
19
|
-
*
|
|
20
|
-
* So the rule and its realization live together now, and a storage adapter calls this
|
|
21
|
-
* instead of re-deriving it. `update: 'forbidden'` is NOT here: refusing a value is a
|
|
22
|
-
* judgment, and the façade already does it (`validation.ts`, patch mode).
|
|
23
|
-
*/
|
|
24
|
-
import { resolveCustomGenerator } from '../field/index.js';
|
|
25
|
-
import { createId } from '@paralleldrive/cuid2';
|
|
26
|
-
/**
|
|
27
|
-
* Resolve a generator TOKEN to a function: a custom name registered via
|
|
28
|
-
* `registerGenerator` wins, then the built-ins. An unknown name throws — loud and
|
|
29
|
-
* local, instead of a silent divergence between two storages.
|
|
30
|
-
*
|
|
31
|
-
* The built-ins used to live storage-side, which put the inversion in plain sight: a
|
|
32
|
-
* generator YOU invent travelled to every adapter, and the three the framework ships
|
|
33
|
-
* did not.
|
|
34
|
-
*/
|
|
35
|
-
function generatorFor(ref) {
|
|
36
|
-
const custom = resolveCustomGenerator(ref);
|
|
37
|
-
if (custom)
|
|
38
|
-
return custom;
|
|
39
|
-
switch (ref) {
|
|
40
|
-
case 'cuid2': return createId;
|
|
41
|
-
case 'uuid': return () => globalThis.crypto.randomUUID();
|
|
42
|
-
case 'nanoid': {
|
|
43
|
-
return () => {
|
|
44
|
-
const bytes = new Uint8Array(21);
|
|
45
|
-
globalThis.crypto.getRandomValues(bytes);
|
|
46
|
-
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
|
|
47
|
-
return Array.from(bytes, (b) => alphabet[b & 63]).join('');
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
default:
|
|
51
|
-
throw new Error(`Unknown generator '${ref}' — register it with registerGenerator('${ref}', fn)`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Fill what the entity says the system writes at creation.
|
|
56
|
-
*
|
|
57
|
-
* A value the caller supplied is never touched, including `null` — presence is the
|
|
58
|
-
* test, not truthiness, so `archivedAt: null` stays null rather than being re-filled.
|
|
59
|
-
*
|
|
60
|
-
* `'now'` produces a `Date`, the value the field declares. The storage converts it if
|
|
61
|
-
* its driver needs something else (`schema-sql/src/values.ts` does exactly that) —
|
|
62
|
-
* which is the same direction every other value travels.
|
|
63
|
-
*/
|
|
64
|
-
export function applyCreate(fields, input) {
|
|
65
|
-
const out = { ...input };
|
|
66
|
-
const instant = Date.now();
|
|
67
|
-
for (const [name, field] of Object.entries(fields)) {
|
|
68
|
-
if (name in out)
|
|
69
|
-
continue;
|
|
70
|
-
const create = field.lifecycle?.create;
|
|
71
|
-
if (create === 'now') {
|
|
72
|
-
// One instant, one Date PER FIELD. `created()` and `updated()` on the same entity
|
|
73
|
-
// would otherwise hold the same object, so mutating `updatedAt` would move an
|
|
74
|
-
// immutable `createdAt` with it — invisible where a storage serializes on write
|
|
75
|
-
// (SQL), lasting where it does not (an in-memory store keeps the row as handed).
|
|
76
|
-
out[name] = new Date(instant);
|
|
77
|
-
}
|
|
78
|
-
else if (typeof create === 'object' && create !== null) {
|
|
79
|
-
if ('value' in create)
|
|
80
|
-
out[name] = freshValue(create.value);
|
|
81
|
-
else if ('generate' in create)
|
|
82
|
-
out[name] = generatorFor(String(create.generate))();
|
|
83
|
-
}
|
|
84
|
-
// `'optional'` and an absent rule both mean: the system writes nothing here.
|
|
85
|
-
}
|
|
86
|
-
return out;
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* A declared default is written into every row, so handing out the declaration itself
|
|
90
|
-
* would alias every row to it — mutate one, mutate the field and all its siblings.
|
|
91
|
-
*
|
|
92
|
-
* Every default the vocabulary can express is a primitive (`text`, `number`, `bool`,
|
|
93
|
-
* `oneOf`; `list` and `json` take none), so this only pays for itself under the escape
|
|
94
|
-
* hatch — `createField({ lifecycle: { create: { value: {…} } } })`. Cheap insurance
|
|
95
|
-
* against the one failure mode nobody would ever debug from the symptom.
|
|
96
|
-
*/
|
|
97
|
-
function freshValue(value) {
|
|
98
|
-
if (value === null || typeof value !== 'object')
|
|
99
|
-
return value;
|
|
100
|
-
return structuredClone(value);
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Fill what the entity says the system writes at every update — `updated()`, and
|
|
104
|
-
* nothing else. A supplied value wins, same rule as create.
|
|
105
|
-
*/
|
|
106
|
-
export function applyUpdate(fields, patch) {
|
|
107
|
-
const out = { ...patch };
|
|
108
|
-
const instant = Date.now();
|
|
109
|
-
for (const [name, field] of Object.entries(fields)) {
|
|
110
|
-
if (field.lifecycle?.update === 'now' && !(name in out))
|
|
111
|
-
out[name] = new Date(instant);
|
|
112
|
-
}
|
|
113
|
-
return out;
|
|
114
|
-
}
|
|
115
|
-
//# sourceMappingURL=lifecycle.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../src/projections/lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAE,sBAAsB,EAA8B,MAAM,mBAAmB,CAAC;AACvF,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAIhD;;;;;;;;GAQG;AACH,SAAS,YAAY,CAAC,GAAW;IAC/B,MAAM,MAAM,GAAG,sBAAsB,CAAC,GAAG,CAAC,CAAC;IAC3C,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,OAAO,EAAE,OAAO,QAAQ,CAAC;QAC9B,KAAK,MAAM,EAAE,OAAO,GAAG,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QACzD,KAAK,QAAQ,EAAE,CAAC;YACd,OAAO,GAAG,EAAE;gBACV,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;gBACjC,UAAU,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;gBACzC,MAAM,QAAQ,GAAG,kEAAkE,CAAC;gBACpF,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC7D,CAAC,CAAC;QACJ,CAAC;QACD;YACE,MAAM,IAAI,KAAK,CAAC,sBAAsB,GAAG,2CAA2C,GAAG,QAAQ,CAAC,CAAC;IACrG,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CAAC,MAAc,EAAE,KAAU;IACpD,MAAM,GAAG,GAAQ,EAAE,GAAG,KAAK,EAAE,CAAC;IAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE3B,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAyB,EAAE,CAAC;QAC3E,IAAI,IAAI,IAAI,GAAG;YAAE,SAAS;QAC1B,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,EAAE,MAAM,CAAC;QACvC,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YACrB,kFAAkF;YAClF,8EAA8E;YAC9E,gFAAgF;YAChF,iFAAiF;YACjF,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC;aAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACzD,IAAI,OAAO,IAAI,MAAM;gBAAE,GAAG,CAAC,IAAI,CAAC,GAAG,UAAU,CAAE,MAA6B,CAAC,KAAK,CAAC,CAAC;iBAC/E,IAAI,UAAU,IAAI,MAAM;gBAAE,GAAG,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,MAAM,CAAE,MAAgC,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;QAChH,CAAC;QACD,6EAA6E;IAC/E,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,UAAU,CAAC,KAAc;IAChC,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9D,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,MAAc,EAAE,KAAU;IACpD,MAAM,GAAG,GAAQ,EAAE,GAAG,KAAK,EAAE,CAAC;IAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE3B,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAyB,EAAE,CAAC;QAC3E,IAAI,KAAK,CAAC,SAAS,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC;IACzF,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC"}
|