@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.
Files changed (319) hide show
  1. package/dist/EntityDeclarations.d.ts +22 -0
  2. package/dist/EntityDeclarations.d.ts.map +1 -0
  3. package/dist/EntityDeclarations.js +19 -0
  4. package/dist/EntityDeclarations.js.map +1 -0
  5. package/dist/Field.d.ts +21 -0
  6. package/dist/Field.d.ts.map +1 -0
  7. package/dist/Field.js +50 -0
  8. package/dist/Field.js.map +1 -0
  9. package/dist/Schema.d.ts +67 -0
  10. package/dist/Schema.d.ts.map +1 -0
  11. package/dist/Schema.js +152 -0
  12. package/dist/Schema.js.map +1 -0
  13. package/dist/SchemaView.d.ts +18 -0
  14. package/dist/SchemaView.d.ts.map +1 -0
  15. package/dist/SchemaView.js +2 -0
  16. package/dist/SchemaView.js.map +1 -0
  17. package/dist/axis/Axis.d.ts +10 -0
  18. package/dist/axis/Axis.d.ts.map +1 -0
  19. package/dist/axis/Axis.js +9 -0
  20. package/dist/axis/Axis.js.map +1 -0
  21. package/dist/axis/Meta.d.ts +4 -0
  22. package/dist/axis/Meta.d.ts.map +1 -0
  23. package/dist/axis/Meta.js +2 -0
  24. package/dist/axis/Meta.js.map +1 -0
  25. package/dist/axis/boundary/Boundaries.d.ts +20 -0
  26. package/dist/axis/boundary/Boundaries.d.ts.map +1 -0
  27. package/dist/axis/boundary/Boundaries.js +31 -0
  28. package/dist/axis/boundary/Boundaries.js.map +1 -0
  29. package/dist/axis/boundary/Boundary.d.ts +26 -0
  30. package/dist/axis/boundary/Boundary.d.ts.map +1 -0
  31. package/dist/axis/boundary/Boundary.js +53 -0
  32. package/dist/axis/boundary/Boundary.js.map +1 -0
  33. package/dist/axis/boundary/axis.d.ts +4 -0
  34. package/dist/axis/boundary/axis.d.ts.map +1 -0
  35. package/dist/axis/boundary/axis.js +29 -0
  36. package/dist/axis/boundary/axis.js.map +1 -0
  37. package/dist/axis/lifecycle/Generators.d.ts +4 -0
  38. package/dist/axis/lifecycle/Generators.d.ts.map +1 -0
  39. package/dist/axis/lifecycle/Generators.js +8 -0
  40. package/dist/axis/lifecycle/Generators.js.map +1 -0
  41. package/dist/axis/lifecycle/Lifecycle.d.ts +28 -0
  42. package/dist/axis/lifecycle/Lifecycle.d.ts.map +1 -0
  43. package/dist/axis/lifecycle/Lifecycle.js +38 -0
  44. package/dist/axis/lifecycle/Lifecycle.js.map +1 -0
  45. package/dist/axis/lifecycle/apply.d.ts +8 -0
  46. package/dist/axis/lifecycle/apply.d.ts.map +1 -0
  47. package/dist/axis/lifecycle/apply.js +72 -0
  48. package/dist/axis/lifecycle/apply.js.map +1 -0
  49. package/dist/axis/lifecycle/axis.d.ts +4 -0
  50. package/dist/axis/lifecycle/axis.d.ts.map +1 -0
  51. package/dist/axis/lifecycle/axis.js +46 -0
  52. package/dist/axis/lifecycle/axis.js.map +1 -0
  53. package/dist/axis/role/Relation.d.ts +10 -0
  54. package/dist/axis/role/Relation.d.ts.map +1 -0
  55. package/dist/axis/role/Relation.js +6 -0
  56. package/dist/axis/role/Relation.js.map +1 -0
  57. package/dist/axis/role/Role.d.ts +38 -0
  58. package/dist/axis/role/Role.d.ts.map +1 -0
  59. package/dist/axis/role/Role.js +50 -0
  60. package/dist/axis/role/Role.js.map +1 -0
  61. package/dist/axis/role/axis.d.ts +5 -0
  62. package/dist/axis/role/axis.d.ts.map +1 -0
  63. package/dist/axis/role/axis.js +101 -0
  64. package/dist/axis/role/axis.js.map +1 -0
  65. package/dist/axis/shape/Formats.d.ts +8 -0
  66. package/dist/axis/shape/Formats.d.ts.map +1 -0
  67. package/dist/axis/shape/Formats.js +10 -0
  68. package/dist/axis/shape/Formats.js.map +1 -0
  69. package/dist/axis/shape/Shape.d.ts +58 -0
  70. package/dist/axis/shape/Shape.d.ts.map +1 -0
  71. package/dist/axis/shape/Shape.js +52 -0
  72. package/dist/axis/shape/Shape.js.map +1 -0
  73. package/dist/card/Descriptor.d.ts +55 -0
  74. package/dist/card/Descriptor.d.ts.map +1 -0
  75. package/dist/card/Descriptor.js +2 -0
  76. package/dist/card/Descriptor.js.map +1 -0
  77. package/dist/card/describe.d.ts +12 -0
  78. package/dist/card/describe.d.ts.map +1 -0
  79. package/dist/card/describe.js +83 -0
  80. package/dist/card/describe.js.map +1 -0
  81. package/dist/card/diff.d.ts +140 -0
  82. package/dist/card/diff.d.ts.map +1 -0
  83. package/dist/card/diff.js +123 -0
  84. package/dist/card/diff.js.map +1 -0
  85. package/dist/card/reconstruct.d.ts +11 -0
  86. package/dist/card/reconstruct.d.ts.map +1 -0
  87. package/dist/card/reconstruct.js +83 -0
  88. package/dist/card/reconstruct.js.map +1 -0
  89. package/dist/card/typescript.d.ts +17 -0
  90. package/dist/card/typescript.d.ts.map +1 -0
  91. package/dist/card/typescript.js +93 -0
  92. package/dist/card/typescript.js.map +1 -0
  93. package/dist/clean.d.ts +2 -0
  94. package/dist/clean.d.ts.map +1 -0
  95. package/dist/{projections/card.js → clean.js} +1 -2
  96. package/dist/clean.js.map +1 -0
  97. package/dist/constraint/FieldGroup.d.ts +21 -0
  98. package/dist/constraint/FieldGroup.d.ts.map +1 -0
  99. package/dist/constraint/FieldGroup.js +65 -0
  100. package/dist/constraint/FieldGroup.js.map +1 -0
  101. package/dist/constraint/Unique.d.ts +4 -0
  102. package/dist/constraint/Unique.d.ts.map +1 -0
  103. package/dist/constraint/Unique.js +4 -0
  104. package/dist/constraint/Unique.js.map +1 -0
  105. package/dist/entity.d.ts +4 -116
  106. package/dist/entity.d.ts.map +1 -1
  107. package/dist/entity.js +13 -289
  108. package/dist/entity.js.map +1 -1
  109. package/dist/index.d.ts +35 -17
  110. package/dist/index.d.ts.map +1 -1
  111. package/dist/index.js +25 -26
  112. package/dist/index.js.map +1 -1
  113. package/dist/judge/Judge.d.ts +14 -0
  114. package/dist/judge/Judge.d.ts.map +1 -0
  115. package/dist/judge/Judge.js +161 -0
  116. package/dist/judge/Judge.js.map +1 -0
  117. package/dist/judge/cases.d.ts +50 -0
  118. package/dist/judge/cases.d.ts.map +1 -0
  119. package/dist/judge/cases.js +100 -0
  120. package/dist/judge/cases.js.map +1 -0
  121. package/dist/judge/form.d.ts +11 -0
  122. package/dist/judge/form.d.ts.map +1 -0
  123. package/dist/judge/form.js +18 -0
  124. package/dist/judge/form.js.map +1 -0
  125. package/dist/judge/options.d.ts +4 -0
  126. package/dist/judge/options.d.ts.map +1 -0
  127. package/dist/judge/options.js +2 -0
  128. package/dist/judge/options.js.map +1 -0
  129. package/dist/judge/result.d.ts +17 -0
  130. package/dist/judge/result.d.ts.map +1 -0
  131. package/dist/judge/result.js +2 -0
  132. package/dist/judge/result.js.map +1 -0
  133. package/dist/name.d.ts +1 -14
  134. package/dist/name.d.ts.map +1 -1
  135. package/dist/name.js +3 -14
  136. package/dist/name.js.map +1 -1
  137. package/dist/projection/encode.d.ts +3 -0
  138. package/dist/projection/encode.d.ts.map +1 -0
  139. package/dist/projection/encode.js +19 -0
  140. package/dist/projection/encode.js.map +1 -0
  141. package/dist/projection/io.d.ts +4 -0
  142. package/dist/projection/io.d.ts.map +1 -0
  143. package/dist/projection/io.js +29 -0
  144. package/dist/projection/io.js.map +1 -0
  145. package/dist/projection/source.d.ts +7 -0
  146. package/dist/projection/source.d.ts.map +1 -0
  147. package/dist/projection/source.js +11 -0
  148. package/dist/projection/source.js.map +1 -0
  149. package/dist/{projections → projection}/standard.d.ts +0 -8
  150. package/dist/projection/standard.d.ts.map +1 -0
  151. package/dist/projection/standard.js +2 -0
  152. package/dist/projection/standard.js.map +1 -0
  153. package/dist/same.d.ts +6 -0
  154. package/dist/same.d.ts.map +1 -0
  155. package/dist/same.js +35 -0
  156. package/dist/same.js.map +1 -0
  157. package/dist/vocabulary/bool.d.ts +1 -1
  158. package/dist/vocabulary/bool.d.ts.map +1 -1
  159. package/dist/vocabulary/bool.js +2 -2
  160. package/dist/vocabulary/bool.js.map +1 -1
  161. package/dist/vocabulary/created.d.ts +2 -12
  162. package/dist/vocabulary/created.d.ts.map +1 -1
  163. package/dist/vocabulary/created.js +3 -14
  164. package/dist/vocabulary/created.js.map +1 -1
  165. package/dist/vocabulary/date.d.ts +1 -1
  166. package/dist/vocabulary/date.d.ts.map +1 -1
  167. package/dist/vocabulary/date.js +2 -2
  168. package/dist/vocabulary/date.js.map +1 -1
  169. package/dist/vocabulary/email.d.ts +1 -2
  170. package/dist/vocabulary/email.d.ts.map +1 -1
  171. package/dist/vocabulary/email.js +0 -1
  172. package/dist/vocabulary/email.js.map +1 -1
  173. package/dist/vocabulary/immutable.d.ts +2 -8
  174. package/dist/vocabulary/immutable.d.ts.map +1 -1
  175. package/dist/vocabulary/immutable.js +4 -10
  176. package/dist/vocabulary/immutable.js.map +1 -1
  177. package/dist/vocabulary/indexed.d.ts +2 -18
  178. package/dist/vocabulary/indexed.d.ts.map +1 -1
  179. package/dist/vocabulary/indexed.js +2 -20
  180. package/dist/vocabulary/indexed.js.map +1 -1
  181. package/dist/vocabulary/json.d.ts +3 -16
  182. package/dist/vocabulary/json.d.ts.map +1 -1
  183. package/dist/vocabulary/json.js +12 -9
  184. package/dist/vocabulary/json.js.map +1 -1
  185. package/dist/vocabulary/list.d.ts +2 -16
  186. package/dist/vocabulary/list.d.ts.map +1 -1
  187. package/dist/vocabulary/list.js +3 -15
  188. package/dist/vocabulary/list.js.map +1 -1
  189. package/dist/vocabulary/many.d.ts +2 -6
  190. package/dist/vocabulary/many.d.ts.map +1 -1
  191. package/dist/vocabulary/many.js +4 -7
  192. package/dist/vocabulary/many.js.map +1 -1
  193. package/dist/vocabulary/nullable.d.ts +2 -8
  194. package/dist/vocabulary/nullable.d.ts.map +1 -1
  195. package/dist/vocabulary/nullable.js +5 -12
  196. package/dist/vocabulary/nullable.js.map +1 -1
  197. package/dist/vocabulary/number.d.ts +1 -1
  198. package/dist/vocabulary/number.d.ts.map +1 -1
  199. package/dist/vocabulary/number.js +2 -2
  200. package/dist/vocabulary/number.js.map +1 -1
  201. package/dist/vocabulary/oneOf.d.ts +1 -8
  202. package/dist/vocabulary/oneOf.d.ts.map +1 -1
  203. package/dist/vocabulary/oneOf.js +2 -2
  204. package/dist/vocabulary/oneOf.js.map +1 -1
  205. package/dist/vocabulary/optional.d.ts +2 -9
  206. package/dist/vocabulary/optional.d.ts.map +1 -1
  207. package/dist/vocabulary/optional.js +6 -14
  208. package/dist/vocabulary/optional.js.map +1 -1
  209. package/dist/vocabulary/primary.d.ts +4 -17
  210. package/dist/vocabulary/primary.d.ts.map +1 -1
  211. package/dist/vocabulary/primary.js +10 -13
  212. package/dist/vocabulary/primary.js.map +1 -1
  213. package/dist/vocabulary/readOnly.d.ts +2 -9
  214. package/dist/vocabulary/readOnly.d.ts.map +1 -1
  215. package/dist/vocabulary/readOnly.js +5 -11
  216. package/dist/vocabulary/readOnly.js.map +1 -1
  217. package/dist/vocabulary/ref.d.ts +2 -7
  218. package/dist/vocabulary/ref.d.ts.map +1 -1
  219. package/dist/vocabulary/ref.js +3 -8
  220. package/dist/vocabulary/ref.js.map +1 -1
  221. package/dist/vocabulary/text.d.ts +2 -2
  222. package/dist/vocabulary/text.d.ts.map +1 -1
  223. package/dist/vocabulary/text.js +2 -2
  224. package/dist/vocabulary/text.js.map +1 -1
  225. package/dist/vocabulary/unique.d.ts +2 -22
  226. package/dist/vocabulary/unique.d.ts.map +1 -1
  227. package/dist/vocabulary/unique.js +5 -24
  228. package/dist/vocabulary/unique.js.map +1 -1
  229. package/dist/vocabulary/updated.d.ts +2 -7
  230. package/dist/vocabulary/updated.d.ts.map +1 -1
  231. package/dist/vocabulary/updated.js +1 -7
  232. package/dist/vocabulary/updated.js.map +1 -1
  233. package/dist/vocabulary/url.d.ts +1 -2
  234. package/dist/vocabulary/url.d.ts.map +1 -1
  235. package/dist/vocabulary/url.js +0 -1
  236. package/dist/vocabulary/url.js.map +1 -1
  237. package/dist/vocabulary/vocabulary.d.ts +5 -0
  238. package/dist/vocabulary/vocabulary.d.ts.map +1 -0
  239. package/dist/vocabulary/vocabulary.js +31 -0
  240. package/dist/vocabulary/vocabulary.js.map +1 -0
  241. package/dist/vocabulary/writeOnly.d.ts +2 -8
  242. package/dist/vocabulary/writeOnly.d.ts.map +1 -1
  243. package/dist/vocabulary/writeOnly.js +5 -10
  244. package/dist/vocabulary/writeOnly.js.map +1 -1
  245. package/package.json +2 -2
  246. package/dist/field/boundary.d.ts +0 -86
  247. package/dist/field/boundary.d.ts.map +0 -1
  248. package/dist/field/boundary.js +0 -94
  249. package/dist/field/boundary.js.map +0 -1
  250. package/dist/field/field.d.ts +0 -64
  251. package/dist/field/field.d.ts.map +0 -1
  252. package/dist/field/field.js +0 -28
  253. package/dist/field/field.js.map +0 -1
  254. package/dist/field/index.d.ts +0 -7
  255. package/dist/field/index.d.ts.map +0 -1
  256. package/dist/field/index.js +0 -9
  257. package/dist/field/index.js.map +0 -1
  258. package/dist/field/lifecycle.d.ts +0 -40
  259. package/dist/field/lifecycle.d.ts.map +0 -1
  260. package/dist/field/lifecycle.js +0 -27
  261. package/dist/field/lifecycle.js.map +0 -1
  262. package/dist/field/meta.d.ts +0 -9
  263. package/dist/field/meta.d.ts.map +0 -1
  264. package/dist/field/meta.js +0 -7
  265. package/dist/field/meta.js.map +0 -1
  266. package/dist/field/role.d.ts +0 -54
  267. package/dist/field/role.d.ts.map +0 -1
  268. package/dist/field/role.js +0 -55
  269. package/dist/field/role.js.map +0 -1
  270. package/dist/field/shape.d.ts +0 -77
  271. package/dist/field/shape.d.ts.map +0 -1
  272. package/dist/field/shape.js +0 -49
  273. package/dist/field/shape.js.map +0 -1
  274. package/dist/hints.d.ts +0 -33
  275. package/dist/hints.d.ts.map +0 -1
  276. package/dist/hints.js +0 -2
  277. package/dist/hints.js.map +0 -1
  278. package/dist/projections/card.d.ts +0 -117
  279. package/dist/projections/card.d.ts.map +0 -1
  280. package/dist/projections/card.js.map +0 -1
  281. package/dist/projections/describe.d.ts +0 -25
  282. package/dist/projections/describe.d.ts.map +0 -1
  283. package/dist/projections/describe.js +0 -136
  284. package/dist/projections/describe.js.map +0 -1
  285. package/dist/projections/encode.d.ts +0 -11
  286. package/dist/projections/encode.d.ts.map +0 -1
  287. package/dist/projections/encode.js +0 -26
  288. package/dist/projections/encode.js.map +0 -1
  289. package/dist/projections/io.d.ts +0 -30
  290. package/dist/projections/io.d.ts.map +0 -1
  291. package/dist/projections/io.js +0 -52
  292. package/dist/projections/io.js.map +0 -1
  293. package/dist/projections/lifecycle.d.ts +0 -43
  294. package/dist/projections/lifecycle.d.ts.map +0 -1
  295. package/dist/projections/lifecycle.js +0 -115
  296. package/dist/projections/lifecycle.js.map +0 -1
  297. package/dist/projections/reconstruct.d.ts +0 -36
  298. package/dist/projections/reconstruct.d.ts.map +0 -1
  299. package/dist/projections/reconstruct.js +0 -149
  300. package/dist/projections/reconstruct.js.map +0 -1
  301. package/dist/projections/source.d.ts +0 -41
  302. package/dist/projections/source.d.ts.map +0 -1
  303. package/dist/projections/source.js +0 -37
  304. package/dist/projections/source.js.map +0 -1
  305. package/dist/projections/standard.d.ts.map +0 -1
  306. package/dist/projections/standard.js +0 -10
  307. package/dist/projections/standard.js.map +0 -1
  308. package/dist/projections/typescript.d.ts +0 -53
  309. package/dist/projections/typescript.d.ts.map +0 -1
  310. package/dist/projections/typescript.js +0 -184
  311. package/dist/projections/typescript.js.map +0 -1
  312. package/dist/projections/validation.d.ts +0 -57
  313. package/dist/projections/validation.d.ts.map +0 -1
  314. package/dist/projections/validation.js +0 -148
  315. package/dist/projections/validation.js.map +0 -1
  316. package/dist/unique.d.ts +0 -65
  317. package/dist/unique.d.ts.map +0 -1
  318. package/dist/unique.js +0 -80
  319. package/dist/unique.js.map +0 -1
@@ -1,15 +1,7 @@
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
- }
1
+ import { vocabulary } from './vocabulary.js';
2
+ import { Anatomy } from '../axis/shape/Shape.js';
3
+ export const optional = vocabulary('optional', (field) => ({
4
+ shape: Anatomy.nullable(field.shape),
5
+ ...(field.lifecycle?.create === undefined ? { lifecycle: { create: 'optional' } } : {}),
6
+ }));
15
7
  //# sourceMappingURL=optional.js.map
@@ -1 +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"}
1
+ {"version":3,"file":"optional.js","sourceRoot":"","sources":["../../src/vocabulary/optional.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAGjD,MAAM,CAAC,MAAM,QAAQ,GAA4C,UAAU,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAClG,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC;IACpC,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,UAAmB,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;CACjG,CAAC,CAAC,CAAC"}
@@ -1,22 +1,9 @@
1
- import { type Field, type GeneratorRef } from '../field/index.js';
1
+ import { Field } from '../Field.js';
2
+ import { type GeneratorRef } from '../axis/lifecycle/Generators.js';
2
3
  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
4
  generate?: GeneratorRef | [name: string, fn: () => string];
9
5
  }
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>;
6
+ export declare function primary(opts?: PrimaryOptions): Field<string>;
7
+ export declare function primary<T>(field: Field<T>): Field<T>;
21
8
  export {};
22
9
  //# sourceMappingURL=primary.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"primary.d.ts","sourceRoot":"","sources":["../../src/vocabulary/primary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAGvF,UAAU,cAAc;IACtB,QAAQ,CAAC,EAAE,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,MAAM,CAAC,CAAC;CAC5D;AAED,wBAAgB,OAAO,CAAC,IAAI,CAAC,EAAE,cAAc,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;AAC9D,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC"}
@@ -1,16 +1,14 @@
1
- import { cloneField, createField, registerGenerator, } from '../field/index.js';
1
+ import { Field } from '../Field.js';
2
+ import { registerGenerator } from '../axis/lifecycle/Generators.js';
3
+ import { Judge } from '../judge/Judge.js';
2
4
  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') {
5
+ if (Judge.isField(fieldOrOptions)) {
7
6
  const field = fieldOrOptions;
8
- return cloneField(field, {
7
+ return field.with({
9
8
  role: { ...field.role, primary: true },
10
- lifecycle: { ...field.lifecycle, update: 'forbidden' },
9
+ lifecycle: { ...field.lifecycle, update: "forbidden" },
11
10
  });
12
11
  }
13
- // primary() or primary({ generate }) — auto-generated string ID
14
12
  const opts = (fieldOrOptions ?? {});
15
13
  let generate;
16
14
  if (Array.isArray(opts.generate)) {
@@ -19,13 +17,12 @@ export function primary(fieldOrOptions) {
19
17
  generate = name;
20
18
  }
21
19
  else {
22
- generate = opts.generate ?? 'cuid2';
20
+ generate = opts.generate ?? "cuid2";
23
21
  }
24
- return createField({
25
- shape: { type: 'string' },
22
+ return new Field({
23
+ shape: { type: "string" },
26
24
  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' },
25
+ lifecycle: { create: { generate }, update: "forbidden" },
29
26
  });
30
27
  }
31
28
  //# sourceMappingURL=primary.js.map
@@ -1 +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"}
1
+ {"version":3,"file":"primary.js","sourceRoot":"","sources":["../../src/vocabulary/primary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAqB,MAAM,iCAAiC,CAAC;AACvF,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAQ1C,MAAM,UAAU,OAAO,CAAC,cAAuC;IAC7D,IAAI,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,cAAc,CAAC;QAC7B,OAAO,KAAK,CAAC,IAAI,CAAC;YAChB,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,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,IAAI,KAAK,CAAS;QACvB,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACzB,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;QACvB,SAAS,EAAE,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE;KACzD,CAAC,CAAC;AACL,CAAC"}
@@ -1,10 +1,3 @@
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>;
1
+ import { Field } from '../Field.js';
2
+ export declare const readOnly: <T>(field: Field<T>) => Field<T>;
10
3
  //# sourceMappingURL=readOnly.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"readOnly.d.ts","sourceRoot":"","sources":["../../src/vocabulary/readOnly.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC,eAAO,MAAM,QAAQ,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAEnD,CAAC"}
@@ -1,12 +1,6 @@
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
- }
1
+ import { vocabulary } from './vocabulary.js';
2
+ import { Boundary } from '../axis/boundary/Boundary.js';
3
+ export const readOnly = vocabulary('readOnly', (field) => ({
4
+ boundary: Boundary.declared(field).with({ in: 'closed' }),
5
+ }));
12
6
  //# sourceMappingURL=readOnly.js.map
@@ -1 +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"}
1
+ {"version":3,"file":"readOnly.js","sourceRoot":"","sources":["../../src/vocabulary/readOnly.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAGxD,MAAM,CAAC,MAAM,QAAQ,GAAqC,UAAU,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC3F,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC;CAC1D,CAAC,CAAC,CAAC"}
@@ -1,12 +1,7 @@
1
- import { type EntityConstructor, type Field } from '../field/index.js';
1
+ import { type EntityConstructor } from '../axis/role/Relation.js';
2
+ import { Field } from '../Field.js';
2
3
  export interface RefOptions {
3
4
  cascade?: boolean;
4
5
  }
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
6
  export declare function ref<E extends EntityConstructor>(target: E | (() => E), opts?: RefOptions): Field<string>;
12
7
  //# sourceMappingURL=ref.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"ref.d.ts","sourceRoot":"","sources":["../../src/vocabulary/ref.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACjF,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,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"}
@@ -1,12 +1,7 @@
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
- */
1
+ import { toTargetThunk } from '../axis/role/Relation.js';
2
+ import { Field } from '../Field.js';
8
3
  export function ref(target, opts) {
9
- return createField({
4
+ return new Field({
10
5
  shape: { type: 'string' },
11
6
  role: {
12
7
  relation: {
@@ -1 +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"}
1
+ {"version":3,"file":"ref.js","sourceRoot":"","sources":["../../src/vocabulary/ref.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA0B,MAAM,0BAA0B,CAAC;AACjF,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAMpC,MAAM,UAAU,GAAG,CAA8B,MAAqB,EAAE,IAAiB;IACvF,OAAO,IAAI,KAAK,CAAS;QACvB,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"}
@@ -1,9 +1,9 @@
1
- import { type Field, type StringFormat } from '../field/index.js';
1
+ import { Field } from '../Field.js';
2
+ import { type StringFormat } from '../axis/shape/Formats.js';
2
3
  export interface TextOptions {
3
4
  min?: number;
4
5
  max?: number;
5
6
  pattern?: string;
6
- /** JSON Schema format predicate (email, uuid, uri, date, time…), asserted by the engine. */
7
7
  format?: StringFormat;
8
8
  default?: string;
9
9
  description?: string;
@@ -1 +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"}
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/vocabulary/text.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,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"}
@@ -1,6 +1,6 @@
1
- import { createField } from '../field/index.js';
1
+ import { Field } from '../Field.js';
2
2
  export function text(opts) {
3
- return createField({
3
+ return new Field({
4
4
  shape: { type: 'string', minLength: opts?.min, maxLength: opts?.max, pattern: opts?.pattern, format: opts?.format },
5
5
  lifecycle: opts?.default !== undefined ? { create: { value: opts.default } } : undefined,
6
6
  meta: opts?.description !== undefined ? { description: opts.description } : undefined,
@@ -1 +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"}
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/vocabulary/text.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAYpC,MAAM,UAAU,IAAI,CAAC,IAAkB;IACrC,OAAO,IAAI,KAAK,CAAS;QACvB,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"}
@@ -1,23 +1,3 @@
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>;
1
+ import { Field } from "../Field.js";
2
+ export declare const unique: <T>(field: Field<T>) => Field<T>;
23
3
  //# sourceMappingURL=unique.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"unique.d.ts","sourceRoot":"","sources":["../../src/vocabulary/unique.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC,eAAO,MAAM,MAAM,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAKnD,CAAC"}
@@ -1,25 +1,6 @@
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
- }
1
+ import { vocabulary } from "./vocabulary.js";
2
+ import { Unique } from "../constraint/Unique.js";
3
+ export const unique = vocabulary("unique", (field) => ({
4
+ role: { rules: [...(field.role?.rules ?? []), new Unique([])] },
5
+ }));
25
6
  //# sourceMappingURL=unique.js.map
@@ -1 +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"}
1
+ {"version":3,"file":"unique.js","sourceRoot":"","sources":["../../src/vocabulary/unique.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEjD,MAAM,CAAC,MAAM,MAAM,GAAqC,UAAU,CAChE,QAAQ,EACR,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACV,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,EAAE,IAAI,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE;CAChE,CAAC,CACH,CAAC"}
@@ -1,8 +1,3 @@
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>;
1
+ import { Field } from '../Field.js';
2
+ export declare function updated(): Field<Date>;
8
3
  //# sourceMappingURL=updated.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"updated.d.ts","sourceRoot":"","sources":["../../src/vocabulary/updated.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC,wBAAgB,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,CAGrC"}
@@ -1,12 +1,6 @@
1
1
  import { created } from './created.js';
2
- import { createField } from '../field/index.js';
3
- /**
4
- * `auto()`, plus the update stamp — the canonical `updatedAt` (Prisma
5
- * `@updatedAt`, Kysely-side `$onUpdate` equivalents). The only new fact is `update: 'now'`;
6
- * shape and creation rule are auto()'s, stated once there.
7
- */
8
2
  export function updated() {
9
3
  const base = created();
10
- return createField({ ...base, lifecycle: { ...base.lifecycle, update: 'now' } });
4
+ return base.with({ lifecycle: { ...base.lifecycle, update: 'now' } });
11
5
  }
12
6
  //# sourceMappingURL=updated.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"updated.js","sourceRoot":"","sources":["../../src/vocabulary/updated.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,WAAW,EAAc,MAAM,mBAAmB,CAAC;AAE5D;;;;GAIG;AACH,MAAM,UAAU,OAAO;IACrB,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,OAAO,WAAW,CAAa,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;AAC/F,CAAC"}
1
+ {"version":3,"file":"updated.js","sourceRoot":"","sources":["../../src/vocabulary/updated.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,MAAM,UAAU,OAAO;IACrB,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;AACxE,CAAC"}
@@ -1,5 +1,4 @@
1
1
  import { type TextOptions } from './text.js';
2
- import type { Field } from '../field/index.js';
3
- /** A string holding a URL — `text({ format: 'uri' })` (JSON Schema's name), named. */
2
+ import type { Field } from '../Field.js';
4
3
  export declare function url(opts?: Omit<TextOptions, 'format'>): Field<string>;
5
4
  //# sourceMappingURL=url.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/vocabulary/url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE/C,sFAAsF;AACtF,wBAAgB,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAErE"}
1
+ {"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/vocabulary/url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,wBAAgB,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAErE"}
@@ -1,5 +1,4 @@
1
1
  import { text } from './text.js';
2
- /** A string holding a URL — `text({ format: 'uri' })` (JSON Schema's name), named. */
3
2
  export function url(opts) {
4
3
  return text({ ...opts, format: 'uri' });
5
4
  }
@@ -1 +1 @@
1
- {"version":3,"file":"url.js","sourceRoot":"","sources":["../../src/vocabulary/url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAoB,MAAM,WAAW,CAAC;AAGnD,sFAAsF;AACtF,MAAM,UAAU,GAAG,CAAC,IAAkC;IACpD,OAAO,IAAI,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;AAC1C,CAAC"}
1
+ {"version":3,"file":"url.js","sourceRoot":"","sources":["../../src/vocabulary/url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAoB,MAAM,WAAW,CAAC;AAGnD,MAAM,UAAU,GAAG,CAAC,IAAkC;IACpD,OAAO,IAAI,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;AAC1C,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { Field } from '../Field.js';
2
+ import type { FieldData } from '../Field.js';
3
+ export declare function vocabulary(name: string, states: (field: Field) => Partial<FieldData>): FieldWord;
4
+ export type FieldWord = (field: Field<any>) => Field<any>;
5
+ //# sourceMappingURL=vocabulary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vocabulary.d.ts","sourceRoot":"","sources":["../../src/vocabulary/vocabulary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAM7C,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,SAAS,CAAC,GAC3C,SAAS,CAEX;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { EXTENSION_AXES } from '../axis/Axis.js';
2
+ import { same } from '../same.js';
3
+ const MEMBER_SLOTS = [...EXTENSION_AXES.map((axis) => axis.slot), 'meta'];
4
+ export function vocabulary(name, states) {
5
+ return (field) => field.with(merge(name, field, states(field)));
6
+ }
7
+ function merge(name, field, stated) {
8
+ const out = {};
9
+ if ('shape' in stated)
10
+ out.shape = stated.shape;
11
+ for (const slot of MEMBER_SLOTS) {
12
+ const members = stated[slot];
13
+ if (members === undefined)
14
+ continue;
15
+ const held = field[slot];
16
+ if (typeof members !== 'object' || members === null || typeof held !== 'object') {
17
+ out[slot] = members;
18
+ continue;
19
+ }
20
+ for (const [member, value] of Object.entries(members)) {
21
+ const previous = held?.[member];
22
+ if (previous === undefined || same(previous, value))
23
+ continue;
24
+ throw new Error(`vocabulary: \`${name}\` states ${slot}.${member} = ${JSON.stringify(value)}, but the ` +
25
+ `field already states ${JSON.stringify(previous)}. Apply one or the other, not both.`);
26
+ }
27
+ out[slot] = { ...held, ...members };
28
+ }
29
+ return out;
30
+ }
31
+ //# sourceMappingURL=vocabulary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vocabulary.js","sourceRoot":"","sources":["../../src/vocabulary/vocabulary.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAElC,MAAM,YAAY,GAAG,CAAC,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,CAAU,CAAC;AAEnF,MAAM,UAAU,UAAU,CACxB,IAAY,EACZ,MAA4C;IAE5C,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAClE,CAAC;AAID,SAAS,KAAK,CAAC,IAAY,EAAE,KAAY,EAAE,MAA0B;IACnE,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,IAAI,OAAO,IAAI,MAAM;QAAE,GAAG,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;IAEhD,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,OAAO,KAAK,SAAS;YAAE,SAAS;QACpC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAwC,CAAC;QAChE,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAChF,GAAG,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC;YACpB,SAAS;QACX,CAAC;QACD,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACtD,MAAM,QAAQ,GAAG,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC;YAChC,IAAI,QAAQ,KAAK,SAAS,IAAI,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC;gBAAE,SAAS;YAC9D,MAAM,IAAI,KAAK,CACb,iBAAiB,IAAI,aAAa,IAAI,IAAI,MAAM,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,YAAY;gBACrF,wBAAwB,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,qCAAqC,CACxF,CAAC;QACJ,CAAC;QACD,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,EAAE,CAAC;IACtC,CAAC;IACD,OAAO,GAAyB,CAAC;AACnC,CAAC"}
@@ -1,9 +1,3 @@
1
- import { type Field } from '../field/index.js';
2
- /**
3
- * Close the field's OUTBOUND direction — accepted at create/patch, never
4
- * emitted (the canonical password). `encodeFields` omits it and the adapters
5
- * exclude it from their output types. The inbound direction (and its
6
- * conversion) is untouched.
7
- */
8
- export declare function writeOnly<T, A extends boolean>(field: Field<T, A>): Field<T, A>;
1
+ import { Field } from '../Field.js';
2
+ export declare const writeOnly: <T>(field: Field<T>) => Field<T>;
9
3
  //# sourceMappingURL=writeOnly.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"writeOnly.d.ts","sourceRoot":"","sources":["../../src/vocabulary/writeOnly.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,KAAK,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE9E;;;;;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"}
1
+ {"version":3,"file":"writeOnly.d.ts","sourceRoot":"","sources":["../../src/vocabulary/writeOnly.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC,eAAO,MAAM,SAAS,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAEpD,CAAC"}
@@ -1,11 +1,6 @@
1
- import { createField, declaredBoundary } from '../field/index.js';
2
- /**
3
- * Close the field's OUTBOUND direction — accepted at create/patch, never
4
- * emitted (the canonical password). `encodeFields` omits it and the adapters
5
- * exclude it from their output types. The inbound direction (and its
6
- * conversion) is untouched.
7
- */
8
- export function writeOnly(field) {
9
- return createField({ ...field, boundary: { ...declaredBoundary(field), out: 'closed' } });
10
- }
1
+ import { vocabulary } from './vocabulary.js';
2
+ import { Boundary } from '../axis/boundary/Boundary.js';
3
+ export const writeOnly = vocabulary('writeOnly', (field) => ({
4
+ boundary: Boundary.declared(field).with({ out: 'closed' }),
5
+ }));
11
6
  //# sourceMappingURL=writeOnly.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"writeOnly.js","sourceRoot":"","sources":["../../src/vocabulary/writeOnly.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAc,MAAM,mBAAmB,CAAC;AAE9E;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAuB,KAAkB;IAChE,OAAO,WAAW,CAAO,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,EAAE,GAAG,gBAAgB,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;AAClG,CAAC"}
1
+ {"version":3,"file":"writeOnly.js","sourceRoot":"","sources":["../../src/vocabulary/writeOnly.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAGxD,MAAM,CAAC,MAAM,SAAS,GAAqC,UAAU,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC7F,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC;CAC3D,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fougere/schema",
3
- "version": "0.2.0-alpha.1",
4
- "description": "The entity and its 4 axes — shape, role, lifecycle, boundary. No engine dependency.",
3
+ "version": "0.3.0-alpha.0",
4
+ "description": "The entity and its 4 axes — shape, role, lifecycle, boundary. One judge, edge-safe.",
5
5
  "keywords": [
6
6
  "fougere",
7
7
  "typescript",
@@ -1,86 +0,0 @@
1
- import type { AnyField } from './field.js';
2
- /**
3
- * Axis 4 · boundary — HOW AND IN WHICH DIRECTION a value crosses the CLIENT frontier.
4
- *
5
- * ⚠️ SCOPE — this axis covers the client frontier ONLY. A direction is meaningless
6
- * unless stated relative to a centre, and this one is relative to the domain facing a
7
- * client: `in` parses a request, `out` renders a response. It does NOT cover storage —
8
- * no storage adapter reads it, which is exactly why `bool`, `list`, `json` and a judged
9
- * `date` cannot be written today (they reach the driver unconverted). The domain↔column
10
- * conversion belongs to the storage adapter, and naming that second frontier is an open
11
- * design question — see the axes study.
12
- *
13
- * The normal form is indexed by DIRECTION. Each direction carries one of the two facets
14
- * of the same frontier:
15
- * a conversion (`{ decode }` / `{ encode }`, a NAMED rule) or the permission
16
- * token `'closed'` — read-only closes `in` (never accepted from a client),
17
- * write-only closes `out` (never emitted, e.g. a password). A key absent →
18
- * that direction is open, identity conversion. Declarative and named — never
19
- * an opaque closure, so adapters stay able to read what a field does.
20
- *
21
- * Two directional registries (decoders, encoders) are the pure base; an alias is
22
- * just a named pair. A field rarely declares a boundary: the default is DERIVED
23
- * from `shape` (convention over config). A declared boundary overrides that
24
- * default PER DIRECTION — closing `out` on a date field leaves the derived
25
- * isoDate decode on `in` intact.
26
- */
27
- /** Inbound: a supplied wire value → domain value. May fail (transformOrFail-style). */
28
- export type Decoder = (value: unknown) => {
29
- value: unknown;
30
- } | {
31
- error: string;
32
- };
33
- /** Outbound: a domain value → wire value. Total — a valid domain value always encodes. */
34
- export type Encoder = (value: unknown) => unknown;
35
- /** The normal form, indexed by direction. Either direction absent → open, identity. */
36
- export interface Boundary {
37
- in?: 'closed' | {
38
- decode: string;
39
- };
40
- out?: 'closed' | {
41
- encode: string;
42
- };
43
- }
44
- /**
45
- * What a field may carry on its `boundary` slot:
46
- * - a string — an ALIAS, resolved to a {@link Boundary} in the alias registry
47
- * - a {@link Boundary} — directional rules directly (allows asymmetry)
48
- *
49
- * The bare string literals are the built-in aliases; `(string & {})` keeps
50
- * autocomplete on them while leaving the set open to `registerBoundaryAlias`.
51
- */
52
- export type BoundaryRef = 'isoDate' | (string & {}) | Boundary;
53
- export declare function registerDecoder(name: string, fn: Decoder): void;
54
- export declare function registerEncoder(name: string, fn: Encoder): void;
55
- /** Register a named boundary — the alias a field can reference. */
56
- export declare function registerBoundaryAlias(name: string, boundary: Boundary): void;
57
- /** The field's DECLARED boundary in normal form (alias resolved) — no derived default. */
58
- export declare function declaredBoundary(field: AnyField): Boundary;
59
- /**
60
- * The field's EFFECTIVE boundary: declared rules win per direction, the
61
- * shape-derived default fills the rest. This is the reader every consumer of
62
- * the axis goes through — `boundaryOf(f).in === 'closed'` is the read-only
63
- * test, `.out === 'closed'` the write-only one.
64
- */
65
- export declare function boundaryOf(field: AnyField): Boundary;
66
- /**
67
- * A field's effective conversion functions. The permission facet is not read
68
- * here: a `'closed'` direction converts as identity — its rejection/omission
69
- * happens in the readers (validation, encode) BEFORE any conversion.
70
- *
71
- * A NAMED codec that no one registered throws, exactly like an unknown alias
72
- * (`declaredBoundary`). It used to fall back to identity: a frond declared
73
- * `{ decode: 'celsius' }`, the consumer never called `registerDecoder`, and the
74
- * value arrived unconverted while the card said it had been converted — the same
75
- * silent loss this repo measured on Remult (`validate: [null, null]`) and on
76
- * zod v4 (a computed default frozen at export). One axis, two spellings, one
77
- * failure mode.
78
- *
79
- * It throws where the conversion is needed, not at boot: nothing walks every
80
- * field at startup. Loud and late beats silent and never.
81
- */
82
- export declare function resolveBoundary(field: AnyField): {
83
- decode: Decoder;
84
- encode: Encoder;
85
- };
86
- //# sourceMappingURL=boundary.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"boundary.d.ts","sourceRoot":"","sources":["../../src/field/boundary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG3C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,uFAAuF;AACvF,MAAM,MAAM,OAAO,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AACjF,0FAA0F;AAC1F,MAAM,MAAM,OAAO,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;AAElD,uFAAuF;AACvF,MAAM,WAAW,QAAQ;IACvB,EAAE,CAAC,EAAE,QAAQ,GAAG;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACnC,GAAG,CAAC,EAAE,QAAQ,GAAG;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CACrC;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,GAAG,QAAQ,CAAC;AAQ/D,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,GAAG,IAAI,CAE/D;AACD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,GAAG,IAAI,CAE/D;AACD,mEAAmE;AACnE,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAE5E;AAgCD,0FAA0F;AAC1F,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,QAAQ,GAAG,QAAQ,CAS1D;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,QAAQ,GAAG,QAAQ,CAIpD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,QAAQ,GAAG;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAMrF"}