@fougere/schema 0.12.0-alpha.0 → 0.12.2-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 (348) hide show
  1. package/dist/FougereFieldAxes.d.ts +3 -0
  2. package/dist/FougereFieldAxes.d.ts.map +1 -0
  3. package/dist/FougereFieldAxes.js +2 -0
  4. package/dist/FougereFieldAxes.js.map +1 -0
  5. package/dist/Schema.d.ts +4 -3
  6. package/dist/Schema.d.ts.map +1 -1
  7. package/dist/Schema.js +3 -0
  8. package/dist/Schema.js.map +1 -1
  9. package/dist/SchemaConstraints.d.ts +13 -10
  10. package/dist/SchemaConstraints.d.ts.map +1 -1
  11. package/dist/SchemaConstraints.js +32 -20
  12. package/dist/SchemaConstraints.js.map +1 -1
  13. package/dist/SchemaDefinition.d.ts.map +1 -1
  14. package/dist/SchemaDefinition.js +6 -5
  15. package/dist/SchemaDefinition.js.map +1 -1
  16. package/dist/SchemaError.d.ts +4 -1
  17. package/dist/SchemaError.d.ts.map +1 -1
  18. package/dist/SchemaError.js +6 -3
  19. package/dist/SchemaError.js.map +1 -1
  20. package/dist/SchemaView.d.ts +3 -2
  21. package/dist/SchemaView.d.ts.map +1 -1
  22. package/dist/axis/Axes.d.ts +16 -0
  23. package/dist/axis/Axes.d.ts.map +1 -0
  24. package/dist/axis/Axes.js +27 -0
  25. package/dist/axis/Axes.js.map +1 -0
  26. package/dist/axis/Axis.d.ts +15 -9
  27. package/dist/axis/Axis.d.ts.map +1 -1
  28. package/dist/axis/Axis.js +1 -10
  29. package/dist/axis/Axis.js.map +1 -1
  30. package/dist/axis/boundary/Boundary.d.ts +38 -5
  31. package/dist/axis/boundary/Boundary.d.ts.map +1 -1
  32. package/dist/axis/boundary/Boundary.js +18 -11
  33. package/dist/axis/boundary/Boundary.js.map +1 -1
  34. package/dist/axis/boundary/Decoder.d.ts +8 -2
  35. package/dist/axis/boundary/Decoder.d.ts.map +1 -1
  36. package/dist/axis/boundary/Decoder.js.map +1 -1
  37. package/dist/axis/lifecycle/Lifecycle.d.ts +29 -8
  38. package/dist/axis/lifecycle/Lifecycle.d.ts.map +1 -1
  39. package/dist/axis/lifecycle/Lifecycle.js +26 -11
  40. package/dist/axis/lifecycle/Lifecycle.js.map +1 -1
  41. package/dist/axis/lifecycle/apply.d.ts.map +1 -1
  42. package/dist/axis/lifecycle/apply.js +3 -4
  43. package/dist/axis/lifecycle/apply.js.map +1 -1
  44. package/dist/axis/role/Role.d.ts +31 -12
  45. package/dist/axis/role/Role.d.ts.map +1 -1
  46. package/dist/axis/role/Role.js +56 -22
  47. package/dist/axis/role/Role.js.map +1 -1
  48. package/dist/axis/shape/Shape.d.ts +32 -9
  49. package/dist/axis/shape/Shape.d.ts.map +1 -1
  50. package/dist/axis/shape/Shape.js +43 -12
  51. package/dist/axis/shape/Shape.js.map +1 -1
  52. package/dist/axis/shape/ShapeFormat.d.ts +10 -0
  53. package/dist/axis/shape/ShapeFormat.d.ts.map +1 -0
  54. package/dist/axis/shape/ShapeFormat.js +32 -0
  55. package/dist/axis/shape/ShapeFormat.js.map +1 -0
  56. package/dist/axis/shape/{Formats.d.ts → StringFormats.d.ts} +3 -3
  57. package/dist/axis/shape/StringFormats.d.ts.map +1 -0
  58. package/dist/axis/shape/{Formats.js → StringFormats.js} +3 -3
  59. package/dist/axis/shape/StringFormats.js.map +1 -0
  60. package/dist/entity/EntityDeclarations.d.ts +3 -2
  61. package/dist/entity/EntityDeclarations.d.ts.map +1 -1
  62. package/dist/entity/FieldGroups.d.ts +9 -0
  63. package/dist/entity/FieldGroups.d.ts.map +1 -0
  64. package/dist/entity/FieldGroups.js +2 -0
  65. package/dist/entity/FieldGroups.js.map +1 -0
  66. package/dist/field/Field.d.ts +37 -12
  67. package/dist/field/Field.d.ts.map +1 -1
  68. package/dist/field/Field.js +55 -8
  69. package/dist/field/Field.js.map +1 -1
  70. package/dist/field/FieldFormat.d.ts +9 -0
  71. package/dist/field/FieldFormat.d.ts.map +1 -0
  72. package/dist/field/FieldFormat.js +12 -0
  73. package/dist/field/FieldFormat.js.map +1 -0
  74. package/dist/field/FieldSet.d.ts +12 -3
  75. package/dist/field/FieldSet.d.ts.map +1 -1
  76. package/dist/field/FieldSet.js +22 -15
  77. package/dist/field/FieldSet.js.map +1 -1
  78. package/dist/index.d.ts +7 -3
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/index.js +4 -3
  81. package/dist/index.js.map +1 -1
  82. package/dist/lib/Format.d.ts +75 -0
  83. package/dist/lib/Format.d.ts.map +1 -0
  84. package/dist/lib/Format.js +123 -0
  85. package/dist/lib/Format.js.map +1 -0
  86. package/dist/lib/JsonSchema.d.ts +2 -0
  87. package/dist/lib/JsonSchema.d.ts.map +1 -0
  88. package/dist/lib/JsonSchema.js +2 -0
  89. package/dist/lib/JsonSchema.js.map +1 -0
  90. package/dist/lib/Registry.d.ts +4 -2
  91. package/dist/lib/Registry.d.ts.map +1 -1
  92. package/dist/lib/Registry.js +11 -5
  93. package/dist/lib/Registry.js.map +1 -1
  94. package/dist/lib/UnionToIntersection.d.ts +7 -0
  95. package/dist/lib/UnionToIntersection.d.ts.map +1 -0
  96. package/dist/lib/UnionToIntersection.js +2 -0
  97. package/dist/lib/UnionToIntersection.js.map +1 -0
  98. package/dist/lib/ValidationResult.d.ts +2 -0
  99. package/dist/lib/ValidationResult.d.ts.map +1 -1
  100. package/dist/lib/ValidationResult.js +6 -0
  101. package/dist/lib/ValidationResult.js.map +1 -1
  102. package/dist/lib/utils.d.ts +1 -1
  103. package/dist/lib/utils.d.ts.map +1 -1
  104. package/dist/lib/utils.js +1 -1
  105. package/dist/lib/utils.js.map +1 -1
  106. package/dist/projection/Cases.d.ts +1 -1
  107. package/dist/projection/Cases.d.ts.map +1 -1
  108. package/dist/projection/Cases.js +20 -8
  109. package/dist/projection/Cases.js.map +1 -1
  110. package/dist/projection/Visibility.d.ts +11 -0
  111. package/dist/projection/Visibility.d.ts.map +1 -1
  112. package/dist/projection/Visibility.js +28 -6
  113. package/dist/projection/Visibility.js.map +1 -1
  114. package/dist/projection/card/Bundle.d.ts.map +1 -1
  115. package/dist/projection/card/Bundle.js.map +1 -1
  116. package/dist/projection/card/Card.d.ts.map +1 -1
  117. package/dist/projection/card/Card.js +24 -30
  118. package/dist/projection/card/Card.js.map +1 -1
  119. package/dist/projection/card/CardForm.d.ts +7 -0
  120. package/dist/projection/card/CardForm.d.ts.map +1 -0
  121. package/dist/projection/card/CardForm.js +2 -0
  122. package/dist/projection/card/CardForm.js.map +1 -0
  123. package/dist/projection/card/CardForms.d.ts +10 -0
  124. package/dist/projection/card/CardForms.d.ts.map +1 -0
  125. package/dist/projection/card/CardForms.js +12 -0
  126. package/dist/projection/card/CardForms.js.map +1 -0
  127. package/dist/projection/card/Change.d.ts +2 -2
  128. package/dist/projection/card/Change.d.ts.map +1 -1
  129. package/dist/projection/card/Diff.js +2 -2
  130. package/dist/projection/card/Diff.js.map +1 -1
  131. package/dist/projection/card/FieldExtension.d.ts +4 -2
  132. package/dist/projection/card/FieldExtension.d.ts.map +1 -1
  133. package/dist/projection/card/RoleDescriptor.d.ts +15 -3
  134. package/dist/projection/card/RoleDescriptor.d.ts.map +1 -1
  135. package/dist/projection/card/RoleDescriptor.js +40 -1
  136. package/dist/projection/card/RoleDescriptor.js.map +1 -1
  137. package/dist/projection/card/ShapeKeywords.d.ts +1 -1
  138. package/dist/projection/card/ShapeKeywords.d.ts.map +1 -1
  139. package/dist/projection/card/admission.d.ts +0 -14
  140. package/dist/projection/card/admission.d.ts.map +1 -1
  141. package/dist/projection/card/admission.js +0 -20
  142. package/dist/projection/card/admission.js.map +1 -1
  143. package/dist/validator/FieldDeclarationValidator.d.ts.map +1 -1
  144. package/dist/validator/FieldDeclarationValidator.js +10 -25
  145. package/dist/validator/FieldDeclarationValidator.js.map +1 -1
  146. package/dist/validator/FieldValueValidator.d.ts.map +1 -1
  147. package/dist/validator/FieldValueValidator.js +9 -29
  148. package/dist/validator/FieldValueValidator.js.map +1 -1
  149. package/dist/validator/InputValidator.d.ts +7 -1
  150. package/dist/validator/InputValidator.d.ts.map +1 -1
  151. package/dist/validator/InputValidator.js +15 -14
  152. package/dist/validator/InputValidator.js.map +1 -1
  153. package/dist/validator/JsonSchemaValidator.d.ts +23 -0
  154. package/dist/validator/JsonSchemaValidator.d.ts.map +1 -0
  155. package/dist/validator/JsonSchemaValidator.js +113 -0
  156. package/dist/validator/JsonSchemaValidator.js.map +1 -0
  157. package/dist/vocabulary/preset/created.d.ts +2 -2
  158. package/dist/vocabulary/preset/created.d.ts.map +1 -1
  159. package/dist/vocabulary/preset/created.js +2 -2
  160. package/dist/vocabulary/preset/created.js.map +1 -1
  161. package/dist/vocabulary/preset/updated.d.ts +2 -2
  162. package/dist/vocabulary/preset/updated.d.ts.map +1 -1
  163. package/dist/vocabulary/preset/updated.js +2 -2
  164. package/dist/vocabulary/preset/updated.js.map +1 -1
  165. package/dist/vocabulary/primitive/bool.d.ts +2 -5
  166. package/dist/vocabulary/primitive/bool.d.ts.map +1 -1
  167. package/dist/vocabulary/primitive/bool.js +1 -5
  168. package/dist/vocabulary/primitive/bool.js.map +1 -1
  169. package/dist/vocabulary/primitive/date.d.ts +2 -3
  170. package/dist/vocabulary/primitive/date.d.ts.map +1 -1
  171. package/dist/vocabulary/primitive/date.js +2 -3
  172. package/dist/vocabulary/primitive/date.js.map +1 -1
  173. package/dist/vocabulary/primitive/json.d.ts +3 -3
  174. package/dist/vocabulary/primitive/json.d.ts.map +1 -1
  175. package/dist/vocabulary/primitive/json.js +11 -11
  176. package/dist/vocabulary/primitive/json.js.map +1 -1
  177. package/dist/vocabulary/primitive/list.d.ts +6 -5
  178. package/dist/vocabulary/primitive/list.d.ts.map +1 -1
  179. package/dist/vocabulary/primitive/list.js +13 -7
  180. package/dist/vocabulary/primitive/list.js.map +1 -1
  181. package/dist/vocabulary/primitive/many.d.ts +2 -4
  182. package/dist/vocabulary/primitive/many.d.ts.map +1 -1
  183. package/dist/vocabulary/primitive/many.js +2 -4
  184. package/dist/vocabulary/primitive/many.js.map +1 -1
  185. package/dist/vocabulary/primitive/number.d.ts +2 -4
  186. package/dist/vocabulary/primitive/number.d.ts.map +1 -1
  187. package/dist/vocabulary/primitive/number.js +1 -3
  188. package/dist/vocabulary/primitive/number.js.map +1 -1
  189. package/dist/vocabulary/primitive/oneOf.d.ts +4 -7
  190. package/dist/vocabulary/primitive/oneOf.d.ts.map +1 -1
  191. package/dist/vocabulary/primitive/oneOf.js +19 -11
  192. package/dist/vocabulary/primitive/oneOf.js.map +1 -1
  193. package/dist/vocabulary/primitive/primary.d.ts +2 -2
  194. package/dist/vocabulary/primitive/primary.d.ts.map +1 -1
  195. package/dist/vocabulary/primitive/primary.js +2 -3
  196. package/dist/vocabulary/primitive/primary.js.map +1 -1
  197. package/dist/vocabulary/primitive/ref.d.ts +2 -6
  198. package/dist/vocabulary/primitive/ref.d.ts.map +1 -1
  199. package/dist/vocabulary/primitive/ref.js +1 -1
  200. package/dist/vocabulary/primitive/ref.js.map +1 -1
  201. package/dist/vocabulary/primitive/text.d.ts +2 -4
  202. package/dist/vocabulary/primitive/text.d.ts.map +1 -1
  203. package/dist/vocabulary/primitive/text.js +1 -3
  204. package/dist/vocabulary/primitive/text.js.map +1 -1
  205. package/dist/vocabulary/rule/nullable.d.ts +0 -1
  206. package/dist/vocabulary/rule/nullable.d.ts.map +1 -1
  207. package/dist/vocabulary/rule/nullable.js +0 -1
  208. package/dist/vocabulary/rule/nullable.js.map +1 -1
  209. package/dist/vocabulary/rule/optional.d.ts +0 -1
  210. package/dist/vocabulary/rule/optional.d.ts.map +1 -1
  211. package/dist/vocabulary/rule/optional.js +0 -1
  212. package/dist/vocabulary/rule/optional.js.map +1 -1
  213. package/dist/vocabulary/rule/readOnly.d.ts +0 -2
  214. package/dist/vocabulary/rule/readOnly.d.ts.map +1 -1
  215. package/dist/vocabulary/rule/readOnly.js +0 -2
  216. package/dist/vocabulary/rule/readOnly.js.map +1 -1
  217. package/dist/vocabulary/rule/writeOnly.d.ts +0 -2
  218. package/dist/vocabulary/rule/writeOnly.d.ts.map +1 -1
  219. package/dist/vocabulary/rule/writeOnly.js +0 -2
  220. package/dist/vocabulary/rule/writeOnly.js.map +1 -1
  221. package/dist/vocabulary/vocabulary.d.ts +2 -3
  222. package/dist/vocabulary/vocabulary.d.ts.map +1 -1
  223. package/dist/vocabulary/vocabulary.js +18 -20
  224. package/dist/vocabulary/vocabulary.js.map +1 -1
  225. package/package.json +1 -1
  226. package/src/FougereFieldAxes.ts +2 -0
  227. package/src/Schema.ts +11 -8
  228. package/src/SchemaConstraints.ts +40 -22
  229. package/src/SchemaDefinition.ts +7 -5
  230. package/src/SchemaError.ts +9 -3
  231. package/src/SchemaView.ts +3 -2
  232. package/src/axis/Axes.ts +34 -0
  233. package/src/axis/Axis.ts +15 -19
  234. package/src/axis/boundary/Boundary.ts +39 -13
  235. package/src/axis/boundary/Decoder.ts +3 -1
  236. package/src/axis/lifecycle/Lifecycle.ts +42 -14
  237. package/src/axis/lifecycle/apply.ts +12 -6
  238. package/src/axis/role/Role.ts +73 -25
  239. package/src/axis/shape/Shape.ts +89 -23
  240. package/src/axis/shape/ShapeFormat.ts +37 -0
  241. package/src/axis/shape/{Formats.ts → StringFormats.ts} +2 -2
  242. package/src/entity/EntityDeclarations.ts +3 -2
  243. package/src/entity/FieldGroups.ts +9 -0
  244. package/src/field/Field.ts +89 -16
  245. package/src/field/FieldFormat.ts +18 -0
  246. package/src/field/FieldSet.ts +27 -19
  247. package/src/index.ts +7 -3
  248. package/src/lib/Format.ts +172 -0
  249. package/src/lib/JsonSchema.ts +1 -0
  250. package/src/lib/Registry.ts +14 -6
  251. package/src/lib/UnionToIntersection.ts +10 -0
  252. package/src/lib/ValidationResult.ts +8 -0
  253. package/src/lib/utils.ts +5 -1
  254. package/src/projection/Cases.ts +25 -8
  255. package/src/projection/Visibility.ts +31 -6
  256. package/src/projection/card/Bundle.ts +2 -0
  257. package/src/projection/card/Card.ts +29 -27
  258. package/src/projection/card/CardForm.ts +8 -0
  259. package/src/projection/card/CardForms.ts +13 -0
  260. package/src/projection/card/Change.ts +2 -2
  261. package/src/projection/card/Diff.ts +6 -2
  262. package/src/projection/card/FieldExtension.ts +5 -2
  263. package/src/projection/card/RoleDescriptor.ts +54 -3
  264. package/src/projection/card/ShapeKeywords.ts +1 -1
  265. package/src/projection/card/admission.ts +0 -33
  266. package/src/validator/FieldDeclarationValidator.ts +10 -25
  267. package/src/validator/FieldValueValidator.ts +13 -36
  268. package/src/validator/InputValidator.ts +17 -12
  269. package/src/validator/JsonSchemaValidator.ts +142 -0
  270. package/src/vocabulary/preset/created.ts +3 -3
  271. package/src/vocabulary/preset/updated.ts +4 -3
  272. package/src/vocabulary/primitive/bool.ts +3 -8
  273. package/src/vocabulary/primitive/date.ts +5 -4
  274. package/src/vocabulary/primitive/json.ts +27 -12
  275. package/src/vocabulary/primitive/list.ts +16 -9
  276. package/src/vocabulary/primitive/many.ts +3 -4
  277. package/src/vocabulary/primitive/number.ts +3 -8
  278. package/src/vocabulary/primitive/oneOf.ts +40 -24
  279. package/src/vocabulary/primitive/primary.ts +9 -5
  280. package/src/vocabulary/primitive/ref.ts +3 -7
  281. package/src/vocabulary/primitive/text.ts +3 -8
  282. package/src/vocabulary/rule/nullable.ts +0 -1
  283. package/src/vocabulary/rule/optional.ts +0 -1
  284. package/src/vocabulary/rule/readOnly.ts +0 -2
  285. package/src/vocabulary/rule/writeOnly.ts +0 -2
  286. package/src/vocabulary/vocabulary.ts +19 -21
  287. package/dist/axis/boundary/BoundaryAxis.d.ts +0 -4
  288. package/dist/axis/boundary/BoundaryAxis.d.ts.map +0 -1
  289. package/dist/axis/boundary/BoundaryAxis.js +0 -30
  290. package/dist/axis/boundary/BoundaryAxis.js.map +0 -1
  291. package/dist/axis/boundary/BoundaryRef.d.ts +0 -3
  292. package/dist/axis/boundary/BoundaryRef.d.ts.map +0 -1
  293. package/dist/axis/boundary/BoundaryRef.js +0 -2
  294. package/dist/axis/boundary/BoundaryRef.js.map +0 -1
  295. package/dist/axis/boundary/BoundaryRules.d.ts +0 -9
  296. package/dist/axis/boundary/BoundaryRules.d.ts.map +0 -1
  297. package/dist/axis/boundary/BoundaryRules.js +0 -2
  298. package/dist/axis/boundary/BoundaryRules.js.map +0 -1
  299. package/dist/axis/lifecycle/Clock.d.ts +0 -10
  300. package/dist/axis/lifecycle/Clock.d.ts.map +0 -1
  301. package/dist/axis/lifecycle/Clock.js +0 -19
  302. package/dist/axis/lifecycle/Clock.js.map +0 -1
  303. package/dist/axis/lifecycle/LifecycleAxis.d.ts +0 -4
  304. package/dist/axis/lifecycle/LifecycleAxis.d.ts.map +0 -1
  305. package/dist/axis/lifecycle/LifecycleAxis.js +0 -52
  306. package/dist/axis/lifecycle/LifecycleAxis.js.map +0 -1
  307. package/dist/axis/lifecycle/LifecycleRules.d.ts +0 -12
  308. package/dist/axis/lifecycle/LifecycleRules.d.ts.map +0 -1
  309. package/dist/axis/lifecycle/LifecycleRules.js +0 -3
  310. package/dist/axis/lifecycle/LifecycleRules.js.map +0 -1
  311. package/dist/axis/role/RoleAxis.d.ts +0 -5
  312. package/dist/axis/role/RoleAxis.d.ts.map +0 -1
  313. package/dist/axis/role/RoleAxis.js +0 -93
  314. package/dist/axis/role/RoleAxis.js.map +0 -1
  315. package/dist/axis/role/RoleRules.d.ts +0 -8
  316. package/dist/axis/role/RoleRules.d.ts.map +0 -1
  317. package/dist/axis/role/RoleRules.js +0 -2
  318. package/dist/axis/role/RoleRules.js.map +0 -1
  319. package/dist/axis/shape/Formats.d.ts.map +0 -1
  320. package/dist/axis/shape/Formats.js.map +0 -1
  321. package/dist/entity/CompositeUnique.d.ts +0 -4
  322. package/dist/entity/CompositeUnique.d.ts.map +0 -1
  323. package/dist/entity/CompositeUnique.js +0 -2
  324. package/dist/entity/CompositeUnique.js.map +0 -1
  325. package/dist/field/Meta.d.ts +0 -4
  326. package/dist/field/Meta.d.ts.map +0 -1
  327. package/dist/field/Meta.js +0 -2
  328. package/dist/field/Meta.js.map +0 -1
  329. package/dist/projection/card/RelationDescriptor.d.ts +0 -5
  330. package/dist/projection/card/RelationDescriptor.d.ts.map +0 -1
  331. package/dist/projection/card/RelationDescriptor.js +0 -2
  332. package/dist/projection/card/RelationDescriptor.js.map +0 -1
  333. package/dist/validator/AdapterFieldValidator.d.ts +0 -14
  334. package/dist/validator/AdapterFieldValidator.d.ts.map +0 -1
  335. package/dist/validator/AdapterFieldValidator.js +0 -37
  336. package/dist/validator/AdapterFieldValidator.js.map +0 -1
  337. package/src/axis/boundary/BoundaryAxis.ts +0 -32
  338. package/src/axis/boundary/BoundaryRef.ts +0 -3
  339. package/src/axis/boundary/BoundaryRules.ts +0 -4
  340. package/src/axis/lifecycle/Clock.ts +0 -22
  341. package/src/axis/lifecycle/LifecycleAxis.ts +0 -55
  342. package/src/axis/lifecycle/LifecycleRules.ts +0 -9
  343. package/src/axis/role/RoleAxis.ts +0 -104
  344. package/src/axis/role/RoleRules.ts +0 -8
  345. package/src/entity/CompositeUnique.ts +0 -6
  346. package/src/field/Meta.ts +0 -3
  347. package/src/projection/card/RelationDescriptor.ts +0 -3
  348. package/src/validator/AdapterFieldValidator.ts +0 -41
@@ -1,24 +1,24 @@
1
1
  import { SchemaError } from '../SchemaError.js';
2
2
 
3
3
  export class Registry<T> {
4
- private readonly entries: Map<string, T>;
4
+ private readonly held: Map<string, T>;
5
5
 
6
6
  constructor(
7
7
  private readonly label: string,
8
8
  private readonly hint?: string,
9
9
  entries?: Iterable<readonly [string, T]>,
10
10
  ) {
11
- this.entries = new Map(entries);
11
+ this.held = new Map(entries);
12
12
  }
13
13
 
14
14
  register(name: string, value: T): T {
15
- this.entries.set(name, value);
15
+ this.held.set(name, value);
16
16
 
17
17
  return value;
18
18
  }
19
19
 
20
20
  find(name: string): T | undefined {
21
- return this.entries.get(name);
21
+ return this.held.get(name);
22
22
  }
23
23
 
24
24
  resolve(name: string, path?: string): T {
@@ -32,7 +32,15 @@ export class Registry<T> {
32
32
  );
33
33
  }
34
34
 
35
- private get names(): string[] {
36
- return [...this.entries.keys()];
35
+ get names(): string[] {
36
+ return [...this.held.keys()];
37
+ }
38
+
39
+ get entries(): [string, T][] {
40
+ return [...this.held];
41
+ }
42
+
43
+ get all(): T[] {
44
+ return [...this.held.values()];
37
45
  }
38
46
  }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * What a union becomes when every member must hold at once — `A | B` → `A & B`, through the
3
+ * contravariance of a parameter position, which is the only place TypeScript folds a union.
4
+ * `Merged<[Post, Draft]>` and `ShapeKeywords` both need it, and each had written it out.
5
+ */
6
+ export type UnionToIntersection<U> = (U extends unknown ? (member: U) => void : never) extends (
7
+ member: infer Held,
8
+ ) => void
9
+ ? Held
10
+ : never;
@@ -9,3 +9,11 @@ export type ValidationResult<T> =
9
9
  * reason the value stays segmented: this one cannot be read back.
10
10
  */
11
11
  export const dotted = (path: readonly string[]): string => path.join('.');
12
+
13
+ /** `#/city/zip` — a JSON Pointer fragment, and `~1`/`~0` are how it spells `/` and `~`. */
14
+ export const locationOf = (instanceLocation: string): string[] =>
15
+ instanceLocation
16
+ .replace(/^#/, '')
17
+ .split('/')
18
+ .filter(Boolean)
19
+ .map((segment) => segment.replace(/~1/g, '/').replace(/~0/g, '~'));
package/src/lib/utils.ts CHANGED
@@ -15,4 +15,8 @@ export function clean<T extends Record<string, unknown>>(obj: T): T {
15
15
  export const isObject = (value: unknown): value is Record<string, unknown> =>
16
16
  typeof value === 'object' && value !== null && !Array.isArray(value);
17
17
 
18
- export const shown = (value: unknown): string => JSON.stringify(value) ?? typeof value;
18
+ export const isArrayOf = <T>(
19
+ values: readonly unknown[],
20
+ is: (value: unknown) => value is T,
21
+ ): values is readonly T[] => values.every(is);
22
+
@@ -16,6 +16,7 @@ import type { ValidationCase } from './ValidationCase.js';
16
16
  function wrongTypeFor(field: Field): unknown {
17
17
  const declared = (field.shape as { type?: string | readonly string[] }).type;
18
18
  const names = Array.isArray(declared) ? declared : [declared];
19
+
19
20
  return names.includes('string') ? 42 : 'not-a-value-of-this-shape';
20
21
  }
21
22
 
@@ -42,6 +43,11 @@ function outOfBoundsFor(field: Field): { why: string; value: unknown }[] {
42
43
  cases.push({ why: 'below minimum', value: shape.minimum - 1 });
43
44
  if (typeof shape.maximum === 'number')
44
45
  cases.push({ why: 'above maximum', value: shape.maximum + 1 });
46
+ if (shape.enum) {
47
+ const members = shape.enum.filter((value) => typeof value === 'number');
48
+
49
+ cases.push({ why: 'outside the stated set', value: Math.max(...members) + 1 });
50
+ }
45
51
  }
46
52
  if (shape?.type === 'array') {
47
53
  if (typeof shape.minItems === 'number' && shape.minItems > 0)
@@ -52,6 +58,7 @@ function outOfBoundsFor(field: Field): { why: string; value: unknown }[] {
52
58
  value: Array.from({ length: shape.maxItems + 1 }, () => null),
53
59
  });
54
60
  }
61
+
55
62
  return cases;
56
63
  }
57
64
 
@@ -64,8 +71,8 @@ function outOfBoundsFor(field: Field): { why: string; value: unknown }[] {
64
71
  export class Cases {
65
72
  private constructor(readonly all: readonly ValidationCase[]) {}
66
73
 
67
- static of(entity: SchemaView, valid: Record<string, unknown>): Cases {
68
- return new Cases(enumerate(entity, valid));
74
+ static of(schema: SchemaView, valid: Record<string, unknown>): Cases {
75
+ return new Cases(enumerate(schema, valid));
69
76
  }
70
77
 
71
78
  static holds(
@@ -74,6 +81,7 @@ export class Cases {
74
81
  ): boolean {
75
82
  if (expected === 'accept') return result.success;
76
83
  if (result.success) return false;
84
+
77
85
  return (result.errors ?? []).some((error) => rejected(error.path) === expected.reject);
78
86
  }
79
87
 
@@ -92,8 +100,8 @@ export class Cases {
92
100
  * FR : lit les quatre axes plutôt qu'une liste tenue à la main : un cas existe parce qu'un
93
101
  * champ déclare la règle qu'il casse.
94
102
  */
95
- function enumerate(entity: SchemaView, valid: Record<string, unknown>): ValidationCase[] {
96
- const fields = entity.getFields();
103
+ function enumerate(schema: SchemaView, valid: Record<string, unknown>): ValidationCase[] {
104
+ const fields = schema.getFields();
97
105
  const validator = InputValidator.of(fields);
98
106
 
99
107
  return [
@@ -124,13 +132,13 @@ function aboutField(
124
132
  const withField = (value: unknown) => ({ ...valid, [name]: value });
125
133
  const cases: ValidationCase[] = [];
126
134
 
127
- if (validator.onAbsent(field) === null && name in valid) {
135
+ if (validator.requires(field) && name in valid) {
128
136
  const input = { ...valid };
129
137
  delete input[name];
130
138
  cases.push({ why: `${name} absent`, input, patch: false, expect: { reject: name } });
131
139
  }
132
140
 
133
- if (Boundary.of(field).readOnly) {
141
+ if (Boundary.of(field).readOnly()) {
134
142
  cases.push({
135
143
  why: `${name} supplied although read-only`,
136
144
  input: withField(wrongTypeFor(field)),
@@ -139,7 +147,16 @@ function aboutField(
139
147
  });
140
148
  }
141
149
 
142
- if (Lifecycle.of(field).immutable && !Role.of(field).isPrimary) {
150
+ if (Role.of(field).isCollection()) {
151
+ cases.push({
152
+ why: `${name} supplied although the other side carries it`,
153
+ input: withField([]),
154
+ patch: false,
155
+ expect: { reject: name },
156
+ });
157
+ }
158
+
159
+ if (Lifecycle.of(field).immutable() && !Role.of(field).isPrimary()) {
143
160
  cases.push({
144
161
  why: `${name} supplied on an update`,
145
162
  input: { [name]: valid[name] ?? wrongTypeFor(field) },
@@ -150,7 +167,7 @@ function aboutField(
150
167
 
151
168
  // A reference names a row that must exist; the caller supplied its id and we do not
152
169
  // get to invent a second one, so the only case we can state about it is the bound one.
153
- if (name in valid && !Role.of(field).isReference) {
170
+ if (name in valid && !Role.of(field).isReference()) {
154
171
  cases.push({
155
172
  why: `${name} of the wrong type`,
156
173
  input: withField(wrongTypeFor(field)),
@@ -15,30 +15,54 @@ export class Visibility {
15
15
  const result: Fields = {};
16
16
  for (const [name, field] of Object.entries(this.fields)) {
17
17
  const role = Role.of(field);
18
- if (role.isPrimary) continue;
19
- if (Lifecycle.of(field).stampedAtCreate) continue;
20
- if (role.isCollection) continue;
21
- if (Boundary.of(field).readOnly) continue;
18
+ if (role.isPrimary()) continue;
19
+ if (Lifecycle.of(field).stampedAtCreate()) continue;
20
+ if (role.isCollection()) continue;
21
+ if (Boundary.of(field).readOnly()) continue;
22
22
  result[name] = field;
23
23
  }
24
+
24
25
  return result;
25
26
  }
26
27
 
27
28
  get output(): Fields {
28
29
  const result: Fields = {};
29
30
  for (const [name, field] of Object.entries(this.fields)) {
30
- if (Boundary.of(field).writeOnly) continue;
31
+ if (Boundary.of(field).writeOnly()) continue;
31
32
  result[name] = field;
32
33
  }
34
+
33
35
  return result;
34
36
  }
35
37
 
38
+ /**
39
+ * The dual of `encode`, read where a caller RECEIVES a row.
40
+ *
41
+ * `date-time` means a `Date` on both sides, and only the outgoing half was ever applied: a
42
+ * handler answered `new Date(0)`, the facade encoded it, and the caller was handed
43
+ * `"1970-01-01T00:00:00.000Z"` while its type said `Date` — locally as well as across a wire.
44
+ *
45
+ * It converts and never judges: what arrives was validated where it was produced, so a value
46
+ * the codec refuses is kept as it came rather than replaced or thrown over.
47
+ */
48
+ decode(record: Record<string, unknown>): Record<string, unknown> {
49
+ const out: Record<string, unknown> = { ...record };
50
+ for (const [key, field] of Object.entries(this.fields)) {
51
+ const value = record[key];
52
+ if (!(key in record) || value === null || value === undefined) continue;
53
+ const verdict = Boundary.of(field).decode(value);
54
+ if ('value' in verdict) out[key] = verdict.value;
55
+ }
56
+
57
+ return out;
58
+ }
59
+
36
60
  encode(record: Record<string, unknown>): Record<string, unknown> {
37
61
  const out: Record<string, unknown> = { ...record };
38
62
  for (const [key, field] of Object.entries(this.fields)) {
39
63
  if (!(key in record)) continue;
40
64
  const boundary = Boundary.of(field);
41
- if (boundary.writeOnly) {
65
+ if (boundary.writeOnly()) {
42
66
  delete out[key];
43
67
  continue;
44
68
  }
@@ -46,6 +70,7 @@ export class Visibility {
46
70
  if (value === null || value === undefined) continue;
47
71
  out[key] = boundary.encode(value);
48
72
  }
73
+
49
74
  return out;
50
75
  }
51
76
  }
@@ -56,6 +56,7 @@ export class Bundle {
56
56
  byName[name.toLowerCase()] = schema as unknown as EntityConstructor;
57
57
  schemas[name] = schema;
58
58
  }
59
+
59
60
  return schemas;
60
61
  }
61
62
 
@@ -72,6 +73,7 @@ export class Bundle {
72
73
  );
73
74
  if (diff.changes.length > 0 || diff.ambiguous.length > 0) entities[name] = diff;
74
75
  }
76
+
75
77
  return {
76
78
  entitiesAdded: Object.keys(after).filter((name) => !(name in before)),
77
79
  entitiesRemoved: Object.keys(before).filter((name) => !(name in after)),
@@ -1,4 +1,5 @@
1
- import { EXTENSION_AXES } from '../../axis/Axis.js';
1
+ import { Axes } from '../../axis/Axes.js';
2
+ import { CardForms } from './CardForms.js';
2
3
  import { type Resolver } from '../../axis/Resolver.js';
3
4
  import { clean, isObject } from '../../lib/utils.js';
4
5
  import { Field } from '../../field/Field.js';
@@ -38,10 +39,11 @@ export class Card<T = Values<Fields>> {
38
39
  const required: string[] = [];
39
40
  for (const [key, field] of Object.entries(fields)) {
40
41
  properties[key] = describeField(field, key);
41
- if (validator.onAbsent(field) === null) required.push(key);
42
+ if (validator.requires(field)) required.push(key);
42
43
  }
43
- for (const group of schema.getUnique() ?? [])
44
- for (const member of group) carryGroup(properties[member], group);
44
+ for (const kind of ['unique', 'index'] as const)
45
+ for (const group of (kind === 'unique' ? schema.getUnique() : schema.getIndex()) ?? [])
46
+ for (const member of group) carryGroup(properties[member], kind, group);
45
47
 
46
48
  const descriptor: SchemaDescriptor = {
47
49
  type: 'object',
@@ -55,6 +57,7 @@ export class Card<T = Values<Fields>> {
55
57
 
56
58
  const origin = originOf(schema);
57
59
  if (origin) descriptor['x-fougere-derived'] = origin;
60
+
58
61
  return new Card<Values<TFields>>(descriptor);
59
62
  }
60
63
 
@@ -94,7 +97,7 @@ export class Card<T = Values<Fields>> {
94
97
  }
95
98
 
96
99
  const fields: Fields = {};
97
- const groups: string[][] = [];
100
+ const groups: { unique: string[][]; index: string[][] } = { unique: [], index: [] };
98
101
  for (const [key, property] of Object.entries(descriptor.properties)) {
99
102
  if (!isObject(property)) {
100
103
  refuse(
@@ -103,13 +106,15 @@ export class Card<T = Values<Fields>> {
103
106
  );
104
107
  }
105
108
  fields[key] = reconstructField(property, key, resolve);
106
- for (const group of property['x-fougere']?.role?.unique ?? [])
107
- if (group.length > 1) groups.push([...group]);
109
+ for (const kind of ['unique', 'index'] as const)
110
+ for (const group of property['x-fougere']?.role?.[kind] ?? [])
111
+ if (group.length > 1) groups[kind].push([...group]);
108
112
  }
109
113
  const schema = Schema.of({ fields, constraints: SchemaConstraints.of(groups) });
110
114
  const title = name ?? descriptor.title;
111
115
  if (title)
112
116
  Object.defineProperty(schema, 'name', { value: title, configurable: true });
117
+
113
118
  return schema as unknown as SchemaConstructor<FieldsOf<T>>;
114
119
  }
115
120
 
@@ -126,13 +131,13 @@ export class Card<T = Values<Fields>> {
126
131
  */
127
132
  function describeExtension(field: Field, key: string): FieldExtension | undefined {
128
133
  const extension: Record<string, unknown> = {};
129
- for (const axis of EXTENSION_AXES) {
130
- const declared = (field as unknown as Record<string, unknown>)[axis.slot];
131
- if (declared === undefined) continue;
132
- const wire = axis.describe(declared, key);
133
- if (wire !== undefined) extension[axis.slot] = wire;
134
+ for (const [name, declared] of Object.entries(field.axes)) {
135
+ const form = CardForms.find(name);
136
+ const wire = form ? form.describe(declared, key) : declared;
137
+ if (wire !== undefined) extension[name] = wire;
134
138
  }
135
139
  clean(extension);
140
+
136
141
  return Object.keys(extension).length ? (extension as FieldExtension) : undefined;
137
142
  }
138
143
 
@@ -140,9 +145,9 @@ function describeField(field: Field, key: string): FieldDescriptor {
140
145
  // The shape IS JSON Schema, so it lands whole rather than key by key — which is what let
141
146
  // it be copied under a computed key into a type that names its own.
142
147
  const descriptor: FieldDescriptor = clean({ ...field.shape }) as FieldDescriptor;
143
- if (field.meta?.description) descriptor.description = field.meta.description;
144
148
  const extension = describeExtension(field, key);
145
149
  if (extension) descriptor['x-fougere'] = extension;
150
+
146
151
  return descriptor;
147
152
  }
148
153
 
@@ -157,6 +162,7 @@ function originOf(schema: SchemaView): DerivedFrom | undefined {
157
162
  const nameOf = Object.fromEntries(
158
163
  Object.entries(derivation.nameOf).map(([key, value]) => [key, value ?? null]),
159
164
  );
165
+
160
166
  return { from: derivation.sourceName, nameOf };
161
167
  }
162
168
 
@@ -175,7 +181,7 @@ function reconstructShape(property: FieldDescriptor): Field['shape'] | undefined
175
181
 
176
182
  // `describeField` writes the shape whole, so it is read whole: a list of keywords here
177
183
  // would be a second inventory, and the one that forgets a keyword loses it in silence.
178
- const { 'x-fougere': _extension, description: _description, ...shape } = property;
184
+ const { 'x-fougere': _extension, ...shape } = property;
179
185
  if (shape.items) shape.items = reconstructShape(shape.items) as FieldDescriptor;
180
186
 
181
187
  return clean(shape) as Field['shape'];
@@ -201,18 +207,13 @@ function reconstructField(
201
207
  admitPatterns(shape, `Field '${key}'`);
202
208
  const extension = property['x-fougere'];
203
209
  const axes: Record<string, unknown> = {};
204
- for (const axis of EXTENSION_AXES) {
205
- const wire = (extension as Record<string, unknown> | undefined)?.[axis.slot];
206
- if (wire !== undefined) axes[axis.slot] = axis.reconstruct(wire, resolve);
210
+ for (const name of Axes.names) {
211
+ const wire = (extension as Record<string, unknown> | undefined)?.[name];
212
+ const form = CardForms.find(name);
213
+ if (wire !== undefined) axes[name] = form ? form.reconstruct(wire, resolve) : wire;
207
214
  }
208
- return new Field({
209
- shape,
210
- ...axes,
211
- meta:
212
- property.description !== undefined
213
- ? { description: property.description }
214
- : undefined,
215
- } as never);
215
+
216
+ return new Field({ shape, ...axes } as never, key);
216
217
  }
217
218
 
218
219
  /**
@@ -222,10 +223,11 @@ function reconstructField(
222
223
  */
223
224
  function carryGroup(
224
225
  property: FieldDescriptor | undefined,
226
+ kind: 'unique' | 'index',
225
227
  group: readonly string[],
226
228
  ): void {
227
229
  if (!property) return;
228
- const extension = (property['x-fougere'] ??= {}) as { role?: { unique?: string[][] } };
230
+ const extension = (property['x-fougere'] ??= {}) as { role?: Record<string, string[][]> };
229
231
  const role = (extension.role ??= {});
230
- (role.unique ??= []).push([...group]);
232
+ (role[kind] ??= []).push([...group]);
231
233
  }
@@ -0,0 +1,8 @@
1
+ import type { Resolver } from '../../axis/Resolver.js';
2
+
3
+ /** How a card writes what an axis declared, and reads it back — stated by the card, not the axis. */
4
+ export interface CardForm<Declared = unknown, Wire = unknown> {
5
+ describe(value: Declared, key: string): Wire | undefined;
6
+
7
+ reconstruct(wire: Wire, resolve?: Resolver): Declared;
8
+ }
@@ -0,0 +1,13 @@
1
+ import { Registry } from '../../lib/Registry.js';
2
+ import type { CardForm } from './CardForm.js';
3
+ import { roleOnCard } from './RoleDescriptor.js';
4
+
5
+ /**
6
+ * The axes a card writes differently from their declaration. An axis absent here travels as
7
+ * itself, which is what `lifecycle`, `boundary` and anything registered from outside do.
8
+ * FR : les axes qu'une carte écrit autrement que déclarés ; les autres voyagent tels quels.
9
+ * `CardForms.register('tenancy', tenancyOnCard)`
10
+ */
11
+ export const CardForms = new Registry<CardForm>('card form', 'call CardForms.register(name, form)', [
12
+ ['role', roleOnCard as CardForm],
13
+ ]);
@@ -1,5 +1,5 @@
1
- import type { BoundaryRef } from '../../axis/boundary/BoundaryRef.js';
2
- import type { LifecycleRules } from '../../axis/lifecycle/LifecycleRules.js';
1
+ import type { BoundaryRef } from '../../axis/boundary/Boundary.js';
2
+ import type { LifecycleRules } from '../../axis/lifecycle/Lifecycle.js';
3
3
  import type { FieldDescriptor } from './FieldDescriptor.js';
4
4
  import type { RoleDescriptor } from './RoleDescriptor.js';
5
5
  import type { TypeSet } from './TypeSet.js';
@@ -1,5 +1,5 @@
1
1
  import { dequal } from 'dequal';
2
- import { EXTENSION_SLOTS } from '../../axis/Axis.js';
2
+ import { Axes } from '../../axis/Axes.js';
3
3
  import type { FieldDescriptor } from './FieldDescriptor.js';
4
4
  import type { FieldExtension } from './FieldExtension.js';
5
5
 
@@ -93,7 +93,7 @@ function restated(
93
93
  before: FieldExtension | undefined,
94
94
  after: FieldExtension | undefined,
95
95
  ): Change[] {
96
- return EXTENSION_SLOTS.filter((axis) => !dequal(before?.[axis], after?.[axis])).map(
96
+ return Axes.names.filter((axis) => !dequal(before?.[axis], after?.[axis])).map(
97
97
  (axis) =>
98
98
  ({
99
99
  kind: 'restated',
@@ -107,6 +107,7 @@ function restated(
107
107
 
108
108
  function shapeOf(descriptor: FieldDescriptor): Record<string, unknown> {
109
109
  const { 'x-fougere': _extension, description: _description, ...shape } = descriptor;
110
+
110
111
  return shape as Record<string, unknown>;
111
112
  }
112
113
 
@@ -118,6 +119,7 @@ function shapeOf(descriptor: FieldDescriptor): Record<string, unknown> {
118
119
  function typesOf(descriptor: FieldDescriptor): TypeSet {
119
120
  const type = descriptor.type;
120
121
  if (type === undefined) return [];
122
+
121
123
  return (Array.isArray(type) ? [...type] : [type]).sort();
122
124
  }
123
125
 
@@ -128,6 +130,7 @@ function typesOf(descriptor: FieldDescriptor): TypeSet {
128
130
  */
129
131
  function boundsOf(descriptor: FieldDescriptor): Record<string, unknown> {
130
132
  const { type: _type, ...rest } = shapeOf(descriptor);
133
+
131
134
  return rest;
132
135
  }
133
136
 
@@ -153,5 +156,6 @@ function candidates(
153
156
  const now = Object.keys(after);
154
157
  const apart = ({ removed: gone, added: appeared }: RenameCandidate): number =>
155
158
  Math.abs(now.indexOf(appeared) - was.indexOf(gone));
159
+
156
160
  return found.sort((a, b) => apart(a) - apart(b));
157
161
  }
@@ -1,9 +1,12 @@
1
- import type { BoundaryRef } from '../../axis/boundary/BoundaryRef.js';
2
- import type { LifecycleRules } from '../../axis/lifecycle/LifecycleRules.js';
1
+ import type { BoundaryRef } from '../../axis/boundary/Boundary.js';
2
+ import type { LifecycleRules } from '../../axis/lifecycle/Lifecycle.js';
3
3
  import type { RoleDescriptor } from './RoleDescriptor.js';
4
4
 
5
5
  export interface FieldExtension {
6
6
  role?: RoleDescriptor;
7
7
  lifecycle?: LifecycleRules;
8
8
  boundary?: BoundaryRef;
9
+
10
+ /** An axis registered from outside writes here, under its own name. */
11
+ [axis: string]: unknown;
9
12
  }
@@ -1,7 +1,58 @@
1
- import type { RoleRules } from '../../axis/role/RoleRules.js';
2
- import type { RelationDescriptor } from './RelationDescriptor.js';
1
+ import type { EntityConstructor } from '../../axis/role/EntityConstructor.js';
2
+ import type { Relation } from '../../axis/role/Relation.js';
3
+ import type { RoleRules } from '../../axis/role/Role.js';
4
+ import type { Resolver } from '../../axis/Resolver.js';
5
+ import { lowerFirst } from '../../lib/utils.js';
6
+ import type { CardForm } from './CardForm.js';
3
7
 
4
- export type RoleDescriptor = Pick<RoleRules, 'primary' | 'index'> & {
8
+ /**
9
+ * What a role becomes on a card: a target is a NAME there, and `unique` and `index` are both
10
+ * GROUPS — the field says `true`, the card says which names go together, since a group of
11
+ * several belongs to no single field.
12
+ */
13
+ export type RoleDescriptor = Pick<RoleRules, 'primary'> & {
5
14
  unique?: string[][];
15
+ index?: string[][];
6
16
  relation?: RelationDescriptor;
7
17
  };
18
+
19
+ export type RelationDescriptor = Pick<Relation, 'kind' | 'onDelete'> & { to: string };
20
+
21
+ type Mutable<T> = { -readonly [K in keyof T]: T[K] };
22
+
23
+ /** A card carries the target's NAME: a class cannot cross a process boundary. */
24
+ export const roleOnCard: CardForm<RoleRules, RoleDescriptor> = {
25
+ describe(role, key) {
26
+ const descriptor: Mutable<RoleDescriptor> = {};
27
+ if (role.primary) descriptor.primary = true;
28
+ if (role.unique) descriptor.unique = [[key]];
29
+ if (role.index) descriptor.index = [[key]];
30
+ if (role.relation) {
31
+ const target = role.relation.to() as { name?: string };
32
+ descriptor.relation = {
33
+ to: lowerFirst(target.name ?? ''),
34
+ kind: role.relation.kind,
35
+ ...(role.relation.onDelete ? { onDelete: role.relation.onDelete } : {}),
36
+ };
37
+ }
38
+
39
+ return Object.keys(descriptor).length ? descriptor : undefined;
40
+ },
41
+
42
+ reconstruct(wire, resolve?: Resolver) {
43
+ const rules: Mutable<RoleRules> = {};
44
+ if (wire.primary) rules.primary = true;
45
+ if (wire.unique?.some((group) => group.length === 1)) rules.unique = true;
46
+ if (wire.index?.some((group) => group.length === 1)) rules.index = true;
47
+ if (wire.relation) {
48
+ const name = wire.relation.to;
49
+ rules.relation = {
50
+ to: () => (resolve?.(name) ?? ({ name } as unknown)) as EntityConstructor,
51
+ kind: wire.relation.kind,
52
+ ...(wire.relation.onDelete ? { onDelete: wire.relation.onDelete } : {}),
53
+ };
54
+ }
55
+
56
+ return rules;
57
+ },
58
+ };
@@ -1,7 +1,7 @@
1
1
  import type { Shape } from '../../axis/shape/Shape.js';
2
+ import type { UnionToIntersection } from '../../lib/UnionToIntersection.js';
2
3
 
3
4
  type KeywordsOf<S> = S extends unknown ? Partial<Omit<S, 'type' | 'items' | 'properties'>> : never;
4
- type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
5
5
 
6
6
  /** Every keyword any shape may carry, flattened — what a descriptor records of one. */
7
7
  export type ShapeKeywords = UnionToIntersection<KeywordsOf<Shape>>;
@@ -1,6 +1,4 @@
1
- import type { ValidationError } from '../../lib/ValidationError.js';
2
1
  import { Shapes } from '../../axis/shape/Shape.js';
3
- import { dotted } from '../../lib/ValidationResult.js';
4
2
  import { SchemaError } from '../../SchemaError.js';
5
3
 
6
4
  /**
@@ -12,37 +10,6 @@ export function refuse(what: string, fix: string): never {
12
10
  throw new SchemaError(`This card cannot be read: ${what}.\n ${fix}`);
13
11
  }
14
12
 
15
- /**
16
- * Runs a card's axis through the validator a hand-written field goes through, so there is
17
- * one judge and not two.
18
- * FR : passe l'axe d'une carte par le juge d'un champ écrit à la main : un juge, pas deux.
19
- * `{ update: 'maybe' }` → `lifecycle is malformed — lifecycle.update: Expected 'now' or 'forbidden'`
20
- */
21
- export function admitWire(
22
- validator: (value: unknown, errors: ValidationError[]) => void,
23
- value: unknown,
24
- slot: string,
25
- ): void {
26
- const errors: ValidationError[] = [];
27
- validator(value, errors);
28
- if (errors.length) {
29
- refuse(
30
- `${slot} is malformed — ${errors.map((e) => `${dotted(e.path)}: ${e.message}`).join('; ')}`,
31
- 'A card states an axis the way a declaration does.',
32
- );
33
- }
34
- }
35
-
36
- /**
37
- * Checks a token against the list that declares it, never against a copy written here.
38
- * FR : vérifie un mot contre la liste qui le déclare, jamais contre une copie locale.
39
- * `oneOfTokens('many', RELATION_KINDS)` → `true`
40
- */
41
- export const oneOfTokens = <T extends readonly string[]>(
42
- value: unknown,
43
- tokens: T,
44
- ): value is T[number] => typeof value === 'string' && (tokens as readonly string[]).includes(value);
45
-
46
13
  /**
47
14
  * Compiles every `pattern` at the facade, so a bad one is refused here and not at the first row.
48
15
  * FR : compile chaque `pattern` à la porte : un mauvais est refusé ici, pas au premier row.