@fougere/schema 0.1.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.
Files changed (171) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +32 -0
  3. package/dist/entity.d.ts +117 -0
  4. package/dist/entity.d.ts.map +1 -0
  5. package/dist/entity.js +285 -0
  6. package/dist/entity.js.map +1 -0
  7. package/dist/field/boundary.d.ts +75 -0
  8. package/dist/field/boundary.d.ts.map +1 -0
  9. package/dist/field/boundary.js +76 -0
  10. package/dist/field/boundary.js.map +1 -0
  11. package/dist/field/field.d.ts +64 -0
  12. package/dist/field/field.d.ts.map +1 -0
  13. package/dist/field/field.js +28 -0
  14. package/dist/field/field.js.map +1 -0
  15. package/dist/field/index.d.ts +7 -0
  16. package/dist/field/index.d.ts.map +1 -0
  17. package/dist/field/index.js +9 -0
  18. package/dist/field/index.js.map +1 -0
  19. package/dist/field/lifecycle.d.ts +40 -0
  20. package/dist/field/lifecycle.d.ts.map +1 -0
  21. package/dist/field/lifecycle.js +27 -0
  22. package/dist/field/lifecycle.js.map +1 -0
  23. package/dist/field/meta.d.ts +9 -0
  24. package/dist/field/meta.d.ts.map +1 -0
  25. package/dist/field/meta.js +7 -0
  26. package/dist/field/meta.js.map +1 -0
  27. package/dist/field/role.d.ts +54 -0
  28. package/dist/field/role.d.ts.map +1 -0
  29. package/dist/field/role.js +55 -0
  30. package/dist/field/role.js.map +1 -0
  31. package/dist/field/shape.d.ts +77 -0
  32. package/dist/field/shape.d.ts.map +1 -0
  33. package/dist/field/shape.js +49 -0
  34. package/dist/field/shape.js.map +1 -0
  35. package/dist/hints.d.ts +33 -0
  36. package/dist/hints.d.ts.map +1 -0
  37. package/dist/hints.js +2 -0
  38. package/dist/hints.js.map +1 -0
  39. package/dist/index.d.ts +36 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +46 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/projections/card.d.ts +117 -0
  44. package/dist/projections/card.d.ts.map +1 -0
  45. package/dist/projections/card.js +8 -0
  46. package/dist/projections/card.js.map +1 -0
  47. package/dist/projections/describe.d.ts +25 -0
  48. package/dist/projections/describe.d.ts.map +1 -0
  49. package/dist/projections/describe.js +129 -0
  50. package/dist/projections/describe.js.map +1 -0
  51. package/dist/projections/encode.d.ts +11 -0
  52. package/dist/projections/encode.d.ts.map +1 -0
  53. package/dist/projections/encode.js +26 -0
  54. package/dist/projections/encode.js.map +1 -0
  55. package/dist/projections/io.d.ts +30 -0
  56. package/dist/projections/io.d.ts.map +1 -0
  57. package/dist/projections/io.js +52 -0
  58. package/dist/projections/io.js.map +1 -0
  59. package/dist/projections/lifecycle.d.ts +43 -0
  60. package/dist/projections/lifecycle.d.ts.map +1 -0
  61. package/dist/projections/lifecycle.js +115 -0
  62. package/dist/projections/lifecycle.js.map +1 -0
  63. package/dist/projections/reconstruct.d.ts +36 -0
  64. package/dist/projections/reconstruct.d.ts.map +1 -0
  65. package/dist/projections/reconstruct.js +149 -0
  66. package/dist/projections/reconstruct.js.map +1 -0
  67. package/dist/projections/source.d.ts +41 -0
  68. package/dist/projections/source.d.ts.map +1 -0
  69. package/dist/projections/source.js +37 -0
  70. package/dist/projections/source.js.map +1 -0
  71. package/dist/projections/standard.d.ts +41 -0
  72. package/dist/projections/standard.d.ts.map +1 -0
  73. package/dist/projections/standard.js +10 -0
  74. package/dist/projections/standard.js.map +1 -0
  75. package/dist/projections/typescript.d.ts +53 -0
  76. package/dist/projections/typescript.d.ts.map +1 -0
  77. package/dist/projections/typescript.js +166 -0
  78. package/dist/projections/typescript.js.map +1 -0
  79. package/dist/projections/validation.d.ts +49 -0
  80. package/dist/projections/validation.d.ts.map +1 -0
  81. package/dist/projections/validation.js +140 -0
  82. package/dist/projections/validation.js.map +1 -0
  83. package/dist/unique.d.ts +65 -0
  84. package/dist/unique.d.ts.map +1 -0
  85. package/dist/unique.js +80 -0
  86. package/dist/unique.js.map +1 -0
  87. package/dist/vocabulary/auto.d.ts +8 -0
  88. package/dist/vocabulary/auto.d.ts.map +1 -0
  89. package/dist/vocabulary/auto.js +13 -0
  90. package/dist/vocabulary/auto.js.map +1 -0
  91. package/dist/vocabulary/bool.d.ts +6 -0
  92. package/dist/vocabulary/bool.d.ts.map +1 -0
  93. package/dist/vocabulary/bool.js +9 -0
  94. package/dist/vocabulary/bool.js.map +1 -0
  95. package/dist/vocabulary/date.d.ts +3 -0
  96. package/dist/vocabulary/date.d.ts.map +1 -0
  97. package/dist/vocabulary/date.js +5 -0
  98. package/dist/vocabulary/date.js.map +1 -0
  99. package/dist/vocabulary/email.d.ts +5 -0
  100. package/dist/vocabulary/email.d.ts.map +1 -0
  101. package/dist/vocabulary/email.js +6 -0
  102. package/dist/vocabulary/email.js.map +1 -0
  103. package/dist/vocabulary/immutable.d.ts +9 -0
  104. package/dist/vocabulary/immutable.d.ts.map +1 -0
  105. package/dist/vocabulary/immutable.js +11 -0
  106. package/dist/vocabulary/immutable.js.map +1 -0
  107. package/dist/vocabulary/indexed.d.ts +19 -0
  108. package/dist/vocabulary/indexed.d.ts.map +1 -0
  109. package/dist/vocabulary/indexed.js +21 -0
  110. package/dist/vocabulary/indexed.js.map +1 -0
  111. package/dist/vocabulary/json.d.ts +18 -0
  112. package/dist/vocabulary/json.d.ts.map +1 -0
  113. package/dist/vocabulary/json.js +16 -0
  114. package/dist/vocabulary/json.js.map +1 -0
  115. package/dist/vocabulary/list.d.ts +21 -0
  116. package/dist/vocabulary/list.d.ts.map +1 -0
  117. package/dist/vocabulary/list.js +21 -0
  118. package/dist/vocabulary/list.js.map +1 -0
  119. package/dist/vocabulary/many.d.ts +8 -0
  120. package/dist/vocabulary/many.d.ts.map +1 -0
  121. package/dist/vocabulary/many.js +12 -0
  122. package/dist/vocabulary/many.js.map +1 -0
  123. package/dist/vocabulary/nullable.d.ts +9 -0
  124. package/dist/vocabulary/nullable.d.ts.map +1 -0
  125. package/dist/vocabulary/nullable.js +13 -0
  126. package/dist/vocabulary/nullable.js.map +1 -0
  127. package/dist/vocabulary/number.d.ts +10 -0
  128. package/dist/vocabulary/number.d.ts.map +1 -0
  129. package/dist/vocabulary/number.js +9 -0
  130. package/dist/vocabulary/number.js.map +1 -0
  131. package/dist/vocabulary/oneOf.d.ts +16 -0
  132. package/dist/vocabulary/oneOf.d.ts.map +1 -0
  133. package/dist/vocabulary/oneOf.js +13 -0
  134. package/dist/vocabulary/oneOf.js.map +1 -0
  135. package/dist/vocabulary/optional.d.ts +10 -0
  136. package/dist/vocabulary/optional.d.ts.map +1 -0
  137. package/dist/vocabulary/optional.js +15 -0
  138. package/dist/vocabulary/optional.js.map +1 -0
  139. package/dist/vocabulary/primary.d.ts +22 -0
  140. package/dist/vocabulary/primary.d.ts.map +1 -0
  141. package/dist/vocabulary/primary.js +31 -0
  142. package/dist/vocabulary/primary.js.map +1 -0
  143. package/dist/vocabulary/readOnly.d.ts +10 -0
  144. package/dist/vocabulary/readOnly.d.ts.map +1 -0
  145. package/dist/vocabulary/readOnly.js +12 -0
  146. package/dist/vocabulary/readOnly.js.map +1 -0
  147. package/dist/vocabulary/ref.d.ts +12 -0
  148. package/dist/vocabulary/ref.d.ts.map +1 -0
  149. package/dist/vocabulary/ref.js +20 -0
  150. package/dist/vocabulary/ref.js.map +1 -0
  151. package/dist/vocabulary/text.d.ts +12 -0
  152. package/dist/vocabulary/text.d.ts.map +1 -0
  153. package/dist/vocabulary/text.js +9 -0
  154. package/dist/vocabulary/text.js.map +1 -0
  155. package/dist/vocabulary/unique.d.ts +23 -0
  156. package/dist/vocabulary/unique.d.ts.map +1 -0
  157. package/dist/vocabulary/unique.js +25 -0
  158. package/dist/vocabulary/unique.js.map +1 -0
  159. package/dist/vocabulary/updated.d.ts +8 -0
  160. package/dist/vocabulary/updated.d.ts.map +1 -0
  161. package/dist/vocabulary/updated.js +12 -0
  162. package/dist/vocabulary/updated.js.map +1 -0
  163. package/dist/vocabulary/url.d.ts +5 -0
  164. package/dist/vocabulary/url.d.ts.map +1 -0
  165. package/dist/vocabulary/url.js +6 -0
  166. package/dist/vocabulary/url.js.map +1 -0
  167. package/dist/vocabulary/writeOnly.d.ts +9 -0
  168. package/dist/vocabulary/writeOnly.d.ts.map +1 -0
  169. package/dist/vocabulary/writeOnly.js +11 -0
  170. package/dist/vocabulary/writeOnly.js.map +1 -0
  171. package/package.json +40 -0
@@ -0,0 +1,9 @@
1
+ import { type Field } from '../field/index.js';
2
+ /**
3
+ * Forbid re-writing after creation — `update: 'forbidden'` on the lifecycle
4
+ * axis. Creation is untouched; supplying the field in a patch is a validation
5
+ * error ("Immutable"). Derived views (`partial()`) keep the field for
6
+ * introspection — the lifecycle rule, not the view, is what rejects it.
7
+ */
8
+ export declare function immutable<T, A extends boolean>(field: Field<T, A>): Field<T, A>;
9
+ //# sourceMappingURL=immutable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"immutable.d.ts","sourceRoot":"","sources":["../../src/vocabulary/immutable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE5D;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,CAAC,SAAS,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAE/E"}
@@ -0,0 +1,11 @@
1
+ import { createField } from '../field/index.js';
2
+ /**
3
+ * Forbid re-writing after creation — `update: 'forbidden'` on the lifecycle
4
+ * axis. Creation is untouched; supplying the field in a patch is a validation
5
+ * error ("Immutable"). Derived views (`partial()`) keep the field for
6
+ * introspection — the lifecycle rule, not the view, is what rejects it.
7
+ */
8
+ export function immutable(field) {
9
+ return createField({ ...field, lifecycle: { ...field.lifecycle, update: 'forbidden' } });
10
+ }
11
+ //# sourceMappingURL=immutable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"immutable.js","sourceRoot":"","sources":["../../src/vocabulary/immutable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAc,MAAM,mBAAmB,CAAC;AAE5D;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAuB,KAAkB;IAChE,OAAO,WAAW,CAAO,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC;AACjG,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { type Field } from '../field/index.js';
2
+ /**
3
+ * Reads filter on this field often — `index: true` on the role axis.
4
+ *
5
+ * Named `indexed` rather than `index`: `index` is taken everywhere in JavaScript (an
6
+ * array position, a module's entry point), and a vocabulary word has to read as what it
7
+ * says at the call site — `indexed(text())` states a property of the field, `index(...)`
8
+ * would read as an action on it.
9
+ *
10
+ * It changes no answer, only the cost of getting one — which is why it is the one word
11
+ * here that is a hint rather than a rule. The DDL emits `CREATE INDEX`; nothing else
12
+ * reads it, and nothing should: a query that only works with the index is a query with
13
+ * a bug.
14
+ *
15
+ * `unique()` already implies one — a uniqueness constraint is backed by an index on
16
+ * every engine — so declaring both is redundant, not additive.
17
+ */
18
+ export declare function indexed<T, A extends boolean>(field: Field<T, A>): Field<T, A>;
19
+ //# sourceMappingURL=indexed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexed.d.ts","sourceRoot":"","sources":["../../src/vocabulary/indexed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE5D;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,CAAC,SAAS,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAE7E"}
@@ -0,0 +1,21 @@
1
+ import { createField } from '../field/index.js';
2
+ /**
3
+ * Reads filter on this field often — `index: true` on the role axis.
4
+ *
5
+ * Named `indexed` rather than `index`: `index` is taken everywhere in JavaScript (an
6
+ * array position, a module's entry point), and a vocabulary word has to read as what it
7
+ * says at the call site — `indexed(text())` states a property of the field, `index(...)`
8
+ * would read as an action on it.
9
+ *
10
+ * It changes no answer, only the cost of getting one — which is why it is the one word
11
+ * here that is a hint rather than a rule. The DDL emits `CREATE INDEX`; nothing else
12
+ * reads it, and nothing should: a query that only works with the index is a query with
13
+ * a bug.
14
+ *
15
+ * `unique()` already implies one — a uniqueness constraint is backed by an index on
16
+ * every engine — so declaring both is redundant, not additive.
17
+ */
18
+ export function indexed(field) {
19
+ return createField({ ...field, role: { ...field.role, index: true } });
20
+ }
21
+ //# sourceMappingURL=indexed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexed.js","sourceRoot":"","sources":["../../src/vocabulary/indexed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAc,MAAM,mBAAmB,CAAC;AAE5D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,OAAO,CAAuB,KAAkB;IAC9D,OAAO,WAAW,CAAO,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;AAC/E,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { type Field, type SchemaLike } from '../field/index.js';
2
+ /**
3
+ * A JSON value field.
4
+ *
5
+ * - `json()` — an OPAQUE value: passes validation unchecked.
6
+ * - `json(Entity)` — an embedded value object: the entity's SHAPE projection is
7
+ * inlined as JSON Schema nesting (`properties`/`required`), so the engine
8
+ * validates the nested structure and the portable descriptor carries it verbatim.
9
+ *
10
+ * Entity-only on purpose: an arbitrary live validator could not serialise, an
11
+ * entity's shape can. Only the value axis travels — `role`/`lifecycle`/`boundary` of
12
+ * the embedded fields are stripped (identity, write rules and wire conversion belong
13
+ * to the entity standing alone, not to its embedded VALUE form). Shallow consequence:
14
+ * nested boundaries don't run — a nested date stays its wire string.
15
+ */
16
+ export declare function json<T = unknown>(): Field<T>;
17
+ export declare function json<E extends SchemaLike & (new (...args: any[]) => any)>(of: E): Field<InstanceType<E>>;
18
+ //# sourceMappingURL=json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/vocabulary/json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAI7E;;;;;;;;;;;;;GAaG;AACH,wBAAgB,IAAI,CAAC,CAAC,GAAG,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC;AAC9C,wBAAgB,IAAI,CAAC,CAAC,SAAS,UAAU,GAAG,CAAC,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { createField } from '../field/index.js';
2
+ import { describe } from '../projections/describe.js';
3
+ export function json(of) {
4
+ if (!of)
5
+ return createField({ shape: { type: 'object' } });
6
+ const card = describe(of);
7
+ const properties = {};
8
+ for (const [key, prop] of Object.entries(card.properties)) {
9
+ const { 'x-fougere': _ext, ...shapeOnly } = prop;
10
+ properties[key] = shapeOnly;
11
+ }
12
+ return createField({
13
+ shape: { type: 'object', properties, ...(card.required ? { required: card.required } : {}) },
14
+ });
15
+ }
16
+ //# sourceMappingURL=json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/vocabulary/json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAA+B,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAmBtD,MAAM,UAAU,IAAI,CAAC,EAAe;IAClC,IAAI,CAAC,EAAE;QAAE,OAAO,WAAW,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC3D,MAAM,IAAI,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC1B,MAAM,UAAU,GAA4B,EAAE,CAAC;IAC/C,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAC1D,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,GAAG,SAAS,EAAE,GAAG,IAAuB,CAAC;QACpE,UAAU,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IAC9B,CAAC;IACD,OAAO,WAAW,CAAC;QACjB,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;KAC7F,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { type Field } from '../field/index.js';
2
+ export interface ListOptions {
3
+ /** Minimum number of elements. */
4
+ min?: number;
5
+ /** Maximum number of elements. */
6
+ max?: number;
7
+ }
8
+ /**
9
+ * A list of VALUES — `tags: list(text())`, `scores: list(number())`. The element's
10
+ * shape travels as JSON Schema `items`, so the engine validates every element natively.
11
+ *
12
+ * NOT a relation: `many(Post)` is a role (related rows, no value shape); `list(text())`
13
+ * is a value (an array column/JSON, fully owned by the record).
14
+ *
15
+ * Only the element's `shape` embeds. Its other axes are meaningless per-element and are
16
+ * DROPPED SILENTLY — `list(primary())` is accepted and loses the role. The guard below
17
+ * only rejects a field with no shape at all (a relation). Tightening it to reject a
18
+ * carried role/lifecycle/boundary is open.
19
+ */
20
+ export declare function list<T>(item: Field<T, boolean>, opts?: ListOptions): Field<T[]>;
21
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/vocabulary/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE5D,MAAM,WAAW,WAAW;IAC1B,kCAAkC;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC,CAK/E"}
@@ -0,0 +1,21 @@
1
+ import { createField } from '../field/index.js';
2
+ /**
3
+ * A list of VALUES — `tags: list(text())`, `scores: list(number())`. The element's
4
+ * shape travels as JSON Schema `items`, so the engine validates every element natively.
5
+ *
6
+ * NOT a relation: `many(Post)` is a role (related rows, no value shape); `list(text())`
7
+ * is a value (an array column/JSON, fully owned by the record).
8
+ *
9
+ * Only the element's `shape` embeds. Its other axes are meaningless per-element and are
10
+ * DROPPED SILENTLY — `list(primary())` is accepted and loses the role. The guard below
11
+ * only rejects a field with no shape at all (a relation). Tightening it to reject a
12
+ * carried role/lifecycle/boundary is open.
13
+ */
14
+ export function list(item, opts) {
15
+ if (!item.shape)
16
+ throw new Error('list() takes a value field (text(), number()…) — a relation has no value shape');
17
+ return createField({
18
+ shape: { type: 'array', items: item.shape, minItems: opts?.min, maxItems: opts?.max },
19
+ });
20
+ }
21
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/vocabulary/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAc,MAAM,mBAAmB,CAAC;AAS5D;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,IAAI,CAAI,IAAuB,EAAE,IAAkB;IACjE,IAAI,CAAC,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,gFAAgF,CAAC,CAAC;IACnH,OAAO,WAAW,CAAM;QACtB,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE;KACtF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { type EntityConstructor, type Field } from '../field/index.js';
2
+ /**
3
+ * A collection of related entities — a many-relation role, no value shape of its own.
4
+ * Pass the target class (`many(Post)`) or, for circular/forward references, a thunk
5
+ * (`many(() => Post)`).
6
+ */
7
+ export declare function many<E extends EntityConstructor>(target: E | (() => E)): Field<InstanceType<E>[]>;
8
+ //# sourceMappingURL=many.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"many.d.ts","sourceRoot":"","sources":["../../src/vocabulary/many.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,iBAAiB,EAAE,KAAK,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAEnG;;;;GAIG;AACH,wBAAgB,IAAI,CAAC,CAAC,SAAS,iBAAiB,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAIjG"}
@@ -0,0 +1,12 @@
1
+ import { createField, toTargetThunk } from '../field/index.js';
2
+ /**
3
+ * A collection of related entities — a many-relation role, no value shape of its own.
4
+ * Pass the target class (`many(Post)`) or, for circular/forward references, a thunk
5
+ * (`many(() => Post)`).
6
+ */
7
+ export function many(target) {
8
+ return createField({
9
+ role: { relation: { to: toTargetThunk(target), kind: 'many' } },
10
+ });
11
+ }
12
+ //# sourceMappingURL=many.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"many.js","sourceRoot":"","sources":["../../src/vocabulary/many.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,aAAa,EAAsC,MAAM,mBAAmB,CAAC;AAEnG;;;;GAIG;AACH,MAAM,UAAU,IAAI,CAA8B,MAAqB;IACrE,OAAO,WAAW,CAAoB;QACpC,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,aAAa,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;KAChE,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { type Field } from '../field/index.js';
2
+ /**
3
+ * Make `null` a legal VALUE without touching presence — the field stays required
4
+ * at creation (the missing quadrant: "nullable but required"). The caller must
5
+ * say something, and "nothing to report" (`null`) is an acceptable answer.
6
+ * For null-legal-and-omissible, use `optional()`.
7
+ */
8
+ export declare function nullable<T>(field: Field<T>): Field<T | null>;
9
+ //# sourceMappingURL=nullable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nullable.d.ts","sourceRoot":"","sources":["../../src/vocabulary/nullable.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1E;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAI5D"}
@@ -0,0 +1,13 @@
1
+ import { cloneField, nullableShape } from '../field/index.js';
2
+ /**
3
+ * Make `null` a legal VALUE without touching presence — the field stays required
4
+ * at creation (the missing quadrant: "nullable but required"). The caller must
5
+ * say something, and "nothing to report" (`null`) is an acceptable answer.
6
+ * For null-legal-and-omissible, use `optional()`.
7
+ */
8
+ export function nullable(field) {
9
+ return cloneField(field, {
10
+ shape: field.shape && nullableShape(field.shape),
11
+ });
12
+ }
13
+ //# sourceMappingURL=nullable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nullable.js","sourceRoot":"","sources":["../../src/vocabulary/nullable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,aAAa,EAAc,MAAM,mBAAmB,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAI,KAAe;IACzC,OAAO,UAAU,CAAC,KAAK,EAAE;QACvB,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC;KACjD,CAAoB,CAAC;AACxB,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { type Field } from '../field/index.js';
2
+ export interface NumberOptions {
3
+ min?: number;
4
+ max?: number;
5
+ integer?: boolean;
6
+ default?: number;
7
+ description?: string;
8
+ }
9
+ export declare function number(opts?: NumberOptions): Field<number>;
10
+ //# sourceMappingURL=number.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"number.d.ts","sourceRoot":"","sources":["../../src/vocabulary/number.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,MAAM,CAAC,IAAI,CAAC,EAAE,aAAa,GAAG,KAAK,CAAC,MAAM,CAAC,CAM1D"}
@@ -0,0 +1,9 @@
1
+ import { createField } from '../field/index.js';
2
+ export function number(opts) {
3
+ return createField({
4
+ shape: { type: opts?.integer ? 'integer' : 'number', minimum: opts?.min, maximum: opts?.max },
5
+ lifecycle: opts?.default !== undefined ? { create: { value: opts.default } } : undefined,
6
+ meta: opts?.description !== undefined ? { description: opts.description } : undefined,
7
+ });
8
+ }
9
+ //# sourceMappingURL=number.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"number.js","sourceRoot":"","sources":["../../src/vocabulary/number.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAc,MAAM,mBAAmB,CAAC;AAU5D,MAAM,UAAU,MAAM,CAAC,IAAoB;IACzC,OAAO,WAAW,CAAS;QACzB,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE;QAC7F,SAAS,EAAE,IAAI,EAAE,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS;QACxF,IAAI,EAAE,IAAI,EAAE,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS;KACtF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { type Field } from '../field/index.js';
2
+ interface OneOfOptions<V extends string = string> {
3
+ description?: string;
4
+ /**
5
+ * Value filled at create when the input omits the field (same as text/number/bool).
6
+ *
7
+ * Typed to the members, not to `string`: a default is written into every row without
8
+ * passing the client judge, so `oneOf('draft', 'published', { default: 'typo' })` used
9
+ * to compile and produce rows the entity's own `validate` refuses.
10
+ */
11
+ default?: V;
12
+ }
13
+ export declare function oneOf<const T extends readonly string[]>(...values: [...T]): Field<T[number]>;
14
+ export declare function oneOf<const T extends readonly string[]>(...args: [...T, OneOfOptions<T[number]>]): Field<T[number]>;
15
+ export {};
16
+ //# sourceMappingURL=oneOf.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oneOf.d.ts","sourceRoot":"","sources":["../../src/vocabulary/oneOf.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE5D,UAAU,YAAY,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,CAAC,CAAC;CACb;AAID,wBAAgB,KAAK,CAAC,KAAK,CAAC,CAAC,SAAS,SAAS,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AAC9F,wBAAgB,KAAK,CAAC,KAAK,CAAC,CAAC,SAAS,SAAS,MAAM,EAAE,EACrD,GAAG,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GACvC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { createField } from '../field/index.js';
2
+ export function oneOf(...args) {
3
+ const last = args[args.length - 1];
4
+ const hasOpts = typeof last === 'object' && last !== null && !Array.isArray(last);
5
+ const values = (hasOpts ? args.slice(0, -1) : args);
6
+ const opts = hasOpts ? last : {};
7
+ return createField({
8
+ shape: { type: 'string', enum: values },
9
+ lifecycle: opts.default !== undefined ? { create: { value: opts.default } } : undefined,
10
+ meta: opts.description !== undefined ? { description: opts.description } : undefined,
11
+ });
12
+ }
13
+ //# sourceMappingURL=oneOf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oneOf.js","sourceRoot":"","sources":["../../src/vocabulary/oneOf.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAc,MAAM,mBAAmB,CAAC;AAoB5D,MAAM,UAAU,KAAK,CACnB,GAAG,IAA8C;IAEjD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAClF,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAiC,CAAC;IACpF,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAE,IAAqB,CAAC,CAAC,CAAC,EAAE,CAAC;IACnD,OAAO,WAAW,CAAY;QAC5B,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE;QACvC,SAAS,EAAE,IAAI,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS;QACvF,IAAI,EAAE,IAAI,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS;KACrF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { type Field } from '../field/index.js';
2
+ /**
3
+ * Make a field nullable AND omissible at creation — `null` enters the shape's
4
+ * grammar (the `[T,'null']` union) and the lifecycle permits absence. The two
5
+ * moves are independent: for null-legal-but-still-required, use `nullable()`.
6
+ * An existing create rule (a default) wins over `'optional'` — it already
7
+ * answers absence, and with a value rather than an omission.
8
+ */
9
+ export declare function optional<T>(field: Field<T>): Field<T | null, true>;
10
+ //# sourceMappingURL=optional.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"optional.d.ts","sourceRoot":"","sources":["../../src/vocabulary/optional.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1E;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,IAAI,EAAE,IAAI,CAAC,CAKlE"}
@@ -0,0 +1,15 @@
1
+ import { cloneField, nullableShape } from '../field/index.js';
2
+ /**
3
+ * Make a field nullable AND omissible at creation — `null` enters the shape's
4
+ * grammar (the `[T,'null']` union) and the lifecycle permits absence. The two
5
+ * moves are independent: for null-legal-but-still-required, use `nullable()`.
6
+ * An existing create rule (a default) wins over `'optional'` — it already
7
+ * answers absence, and with a value rather than an omission.
8
+ */
9
+ export function optional(field) {
10
+ return cloneField(field, {
11
+ shape: field.shape && nullableShape(field.shape),
12
+ lifecycle: { ...field.lifecycle, create: field.lifecycle?.create ?? 'optional' },
13
+ });
14
+ }
15
+ //# sourceMappingURL=optional.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"optional.js","sourceRoot":"","sources":["../../src/vocabulary/optional.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,aAAa,EAAc,MAAM,mBAAmB,CAAC;AAE1E;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CAAI,KAAe;IACzC,OAAO,UAAU,CAAC,KAAK,EAAE;QACvB,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC;QAChD,SAAS,EAAE,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,SAAS,EAAE,MAAM,IAAI,UAAU,EAAE;KACjF,CAA0B,CAAC;AAC9B,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { type Field, type GeneratorRef } from '../field/index.js';
2
+ interface PrimaryOptions {
3
+ /**
4
+ * Generator token: a built-in preset, a name registered via
5
+ * `registerGenerator`, or the colocated `[name, fn]` tuple — which registers
6
+ * and names in one gesture (the Field only ever carries the name).
7
+ */
8
+ generate?: GeneratorRef | [name: string, fn: () => string];
9
+ }
10
+ /**
11
+ * Create a primary key field.
12
+ *
13
+ * - `primary()` — auto-generated CUID2 string ID (default)
14
+ * - `primary({ generate: 'uuid' })` — auto-generated UUID v4
15
+ * - `primary({ generate: ['monId', fn] })` — custom generator, registered + named
16
+ * - `primary(field)` — promote any field to primary key (no auto-generation;
17
+ * the value must be supplied, so its `auto` flag is preserved)
18
+ */
19
+ export declare function primary(opts?: PrimaryOptions): Field<string, true>;
20
+ export declare function primary<T, A extends boolean>(field: Field<T, A>): Field<T, A>;
21
+ export {};
22
+ //# sourceMappingURL=primary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"primary.d.ts","sourceRoot":"","sources":["../../src/vocabulary/primary.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,KAAK,EACV,KAAK,YAAY,EAClB,MAAM,mBAAmB,CAAC;AAE3B,UAAU,cAAc;IACtB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,MAAM,CAAC,CAAC;CAC5D;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,IAAI,CAAC,EAAE,cAAc,GAAG,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AACpE,wBAAgB,OAAO,CAAC,CAAC,EAAE,CAAC,SAAS,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { cloneField, createField, registerGenerator, } from '../field/index.js';
2
+ export function primary(fieldOrOptions) {
3
+ // primary(field) — promote an existing field to primary key. cloneField keeps
4
+ // every axis (boundary, meta…); the role gains `primary`, and identity in the
5
+ // graph implies immutability in time: `update: 'forbidden'` (decided 2026-07-15).
6
+ if (fieldOrOptions && '__brand' in fieldOrOptions && fieldOrOptions.__brand === 'fougere_field') {
7
+ const field = fieldOrOptions;
8
+ return cloneField(field, {
9
+ role: { ...field.role, primary: true },
10
+ lifecycle: { ...field.lifecycle, update: 'forbidden' },
11
+ });
12
+ }
13
+ // primary() or primary({ generate }) — auto-generated string ID
14
+ const opts = (fieldOrOptions ?? {});
15
+ let generate;
16
+ if (Array.isArray(opts.generate)) {
17
+ const [name, fn] = opts.generate;
18
+ registerGenerator(name, fn);
19
+ generate = name;
20
+ }
21
+ else {
22
+ generate = opts.generate ?? 'cuid2';
23
+ }
24
+ return createField({
25
+ shape: { type: 'string' },
26
+ role: { primary: true },
27
+ // An id is immutable by default: re-supplying it in a patch is an error.
28
+ lifecycle: { create: { generate }, update: 'forbidden' },
29
+ });
30
+ }
31
+ //# sourceMappingURL=primary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"primary.js","sourceRoot":"","sources":["../../src/vocabulary/primary.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,WAAW,EACX,iBAAiB,GAIlB,MAAM,mBAAmB,CAAC;AAsB3B,MAAM,UAAU,OAAO,CAAC,cAA0C;IAChE,8EAA8E;IAC9E,8EAA8E;IAC9E,kFAAkF;IAClF,IAAI,cAAc,IAAI,SAAS,IAAI,cAAc,IAAI,cAAc,CAAC,OAAO,KAAK,eAAe,EAAE,CAAC;QAChG,MAAM,KAAK,GAAG,cAAc,CAAC;QAC7B,OAAO,UAAU,CAAC,KAAK,EAAE;YACvB,IAAI,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE;YACtC,SAAS,EAAE,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE;SACvD,CAAC,CAAC;IACL,CAAC;IAED,gEAAgE;IAChE,MAAM,IAAI,GAAG,CAAC,cAAc,IAAI,EAAE,CAAmB,CAAC;IACtD,IAAI,QAAsB,CAAC;IAC3B,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC;QACjC,iBAAiB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC5B,QAAQ,GAAG,IAAI,CAAC;IAClB,CAAC;SAAM,CAAC;QACN,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC;IACtC,CAAC;IACD,OAAO,WAAW,CAAe;QAC/B,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACzB,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;QACvB,yEAAyE;QACzE,SAAS,EAAE,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE;KACzD,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { type Field } from '../field/index.js';
2
+ /**
3
+ * Close the field's INBOUND direction — emitted normally, never accepted from
4
+ * a client (a server-owned field, e.g. a computed counter). Supplying it in a
5
+ * create or patch input is a validation error ("Read-only"); its absence is
6
+ * never "Required" (the server provides it). Input types exclude it.
7
+ * The outbound direction (and its conversion) is untouched.
8
+ */
9
+ export declare function readOnly<T, A extends boolean>(field: Field<T, A>): Field<T, A>;
10
+ //# sourceMappingURL=readOnly.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readOnly.d.ts","sourceRoot":"","sources":["../../src/vocabulary/readOnly.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,KAAK,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE9E;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,SAAS,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAE9E"}
@@ -0,0 +1,12 @@
1
+ import { createField, declaredBoundary } from '../field/index.js';
2
+ /**
3
+ * Close the field's INBOUND direction — emitted normally, never accepted from
4
+ * a client (a server-owned field, e.g. a computed counter). Supplying it in a
5
+ * create or patch input is a validation error ("Read-only"); its absence is
6
+ * never "Required" (the server provides it). Input types exclude it.
7
+ * The outbound direction (and its conversion) is untouched.
8
+ */
9
+ export function readOnly(field) {
10
+ return createField({ ...field, boundary: { ...declaredBoundary(field), in: 'closed' } });
11
+ }
12
+ //# sourceMappingURL=readOnly.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readOnly.js","sourceRoot":"","sources":["../../src/vocabulary/readOnly.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAc,MAAM,mBAAmB,CAAC;AAE9E;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CAAuB,KAAkB;IAC/D,OAAO,WAAW,CAAO,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,EAAE,GAAG,gBAAgB,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;AACjG,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { type EntityConstructor, type Field } from '../field/index.js';
2
+ export interface RefOptions {
3
+ cascade?: boolean;
4
+ }
5
+ /**
6
+ * A reference to another entity — a foreign-key string plus a one-relation role.
7
+ * Pass the target class (`ref(Author)`) or, for circular/forward references, a
8
+ * thunk (`ref(() => Author)`). For a nullable FK, wrap it: `optional(ref(Author))`
9
+ * — that path also flips the type to `string | null` (which `ref` alone never did).
10
+ */
11
+ export declare function ref<E extends EntityConstructor>(target: E | (() => E), opts?: RefOptions): Field<string>;
12
+ //# sourceMappingURL=ref.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ref.d.ts","sourceRoot":"","sources":["../../src/vocabulary/ref.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,iBAAiB,EAAE,KAAK,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAEnG,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,CAAC,SAAS,iBAAiB,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,CAWxG"}
@@ -0,0 +1,20 @@
1
+ import { createField, toTargetThunk } from '../field/index.js';
2
+ /**
3
+ * A reference to another entity — a foreign-key string plus a one-relation role.
4
+ * Pass the target class (`ref(Author)`) or, for circular/forward references, a
5
+ * thunk (`ref(() => Author)`). For a nullable FK, wrap it: `optional(ref(Author))`
6
+ * — that path also flips the type to `string | null` (which `ref` alone never did).
7
+ */
8
+ export function ref(target, opts) {
9
+ return createField({
10
+ shape: { type: 'string' },
11
+ role: {
12
+ relation: {
13
+ to: toTargetThunk(target),
14
+ kind: 'one',
15
+ onDelete: opts?.cascade ? 'cascade' : undefined,
16
+ },
17
+ },
18
+ });
19
+ }
20
+ //# sourceMappingURL=ref.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ref.js","sourceRoot":"","sources":["../../src/vocabulary/ref.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,aAAa,EAAsC,MAAM,mBAAmB,CAAC;AAMnG;;;;;GAKG;AACH,MAAM,UAAU,GAAG,CAA8B,MAAqB,EAAE,IAAiB;IACvF,OAAO,WAAW,CAAS;QACzB,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACzB,IAAI,EAAE;YACJ,QAAQ,EAAE;gBACR,EAAE,EAAE,aAAa,CAAC,MAAM,CAAC;gBACzB,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;aAChD;SACF;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { type Field, type StringFormat } from '../field/index.js';
2
+ export interface TextOptions {
3
+ min?: number;
4
+ max?: number;
5
+ pattern?: string;
6
+ /** JSON Schema format predicate (email, uuid, uri, date, time…), asserted by the engine. */
7
+ format?: StringFormat;
8
+ default?: string;
9
+ description?: string;
10
+ }
11
+ export declare function text(opts?: TextOptions): Field<string>;
12
+ //# sourceMappingURL=text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/vocabulary/text.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,KAAK,EAAE,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE/E,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4FAA4F;IAC5F,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,IAAI,CAAC,IAAI,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,CAMtD"}
@@ -0,0 +1,9 @@
1
+ import { createField } from '../field/index.js';
2
+ export function text(opts) {
3
+ return createField({
4
+ shape: { type: 'string', minLength: opts?.min, maxLength: opts?.max, pattern: opts?.pattern, format: opts?.format },
5
+ lifecycle: opts?.default !== undefined ? { create: { value: opts.default } } : undefined,
6
+ meta: opts?.description !== undefined ? { description: opts.description } : undefined,
7
+ });
8
+ }
9
+ //# sourceMappingURL=text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/vocabulary/text.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiC,MAAM,mBAAmB,CAAC;AAY/E,MAAM,UAAU,IAAI,CAAC,IAAkB;IACrC,OAAO,WAAW,CAAS;QACzB,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE;QACnH,SAAS,EAAE,IAAI,EAAE,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS;QACxF,IAAI,EAAE,IAAI,EAAE,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS;KACtF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { type Field } from '../field/index.js';
2
+ /**
3
+ * No two rows may carry the same value — a unique constraint of one, on the role axis.
4
+ *
5
+ * The role axis is where it belongs because uniqueness is a fact about the field's
6
+ * place in the entity, not about the values it accepts: `text({ max: 80 })` says what
7
+ * a slug looks like, `unique(...)` says there is only one of each. A shape cannot
8
+ * express it — judging a single value can never see the other rows.
9
+ *
10
+ * So the realization is the storage's, and only the storage's: the DDL emits the
11
+ * constraint, the database enforces it on every write, including writes Fougere never
12
+ * saw. A collision surfaces as the driver's error, not as a `validate()` failure.
13
+ *
14
+ * It also travels: `describe()` carries `role.unique` on the card, so a consumer in
15
+ * another language reads the same promise — one that is now kept.
16
+ *
17
+ * The group is `[]` — the empty member list denoting THIS field, because a field does not
18
+ * know its own key here: `slug` is named by the object literal `entity({...})` receives,
19
+ * one call up. Keeping it empty rather than filling it in later means nothing to remap
20
+ * when `rename()` moves the key, and one shape shared with the composite form.
21
+ */
22
+ export declare function unique<T, A extends boolean>(field: Field<T, A>): Field<T, A>;
23
+ //# sourceMappingURL=unique.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unique.d.ts","sourceRoot":"","sources":["../../src/vocabulary/unique.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE5D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,CAAC,SAAS,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAE5E"}
@@ -0,0 +1,25 @@
1
+ import { createField } from '../field/index.js';
2
+ /**
3
+ * No two rows may carry the same value — a unique constraint of one, on the role axis.
4
+ *
5
+ * The role axis is where it belongs because uniqueness is a fact about the field's
6
+ * place in the entity, not about the values it accepts: `text({ max: 80 })` says what
7
+ * a slug looks like, `unique(...)` says there is only one of each. A shape cannot
8
+ * express it — judging a single value can never see the other rows.
9
+ *
10
+ * So the realization is the storage's, and only the storage's: the DDL emits the
11
+ * constraint, the database enforces it on every write, including writes Fougere never
12
+ * saw. A collision surfaces as the driver's error, not as a `validate()` failure.
13
+ *
14
+ * It also travels: `describe()` carries `role.unique` on the card, so a consumer in
15
+ * another language reads the same promise — one that is now kept.
16
+ *
17
+ * The group is `[]` — the empty member list denoting THIS field, because a field does not
18
+ * know its own key here: `slug` is named by the object literal `entity({...})` receives,
19
+ * one call up. Keeping it empty rather than filling it in later means nothing to remap
20
+ * when `rename()` moves the key, and one shape shared with the composite form.
21
+ */
22
+ export function unique(field) {
23
+ return createField({ ...field, role: { ...field.role, unique: [[]] } });
24
+ }
25
+ //# sourceMappingURL=unique.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unique.js","sourceRoot":"","sources":["../../src/vocabulary/unique.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAc,MAAM,mBAAmB,CAAC;AAE5D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,MAAM,CAAuB,KAAkB;IAC7D,OAAO,WAAW,CAAO,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AAChF,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { type Field } from '../field/index.js';
2
+ /**
3
+ * `auto()`, plus the update stamp — the canonical `updatedAt` (Prisma
4
+ * `@updatedAt`, Kysely-side `$onUpdate` equivalents). The only new fact is `update: 'now'`;
5
+ * shape and creation rule are auto()'s, stated once there.
6
+ */
7
+ export declare function updated(): Field<Date, true>;
8
+ //# sourceMappingURL=updated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"updated.d.ts","sourceRoot":"","sources":["../../src/vocabulary/updated.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,KAAK,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE5D;;;;GAIG;AACH,wBAAgB,OAAO,IAAI,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAG3C"}