@fougere/schema 0.6.0-alpha.0 → 0.7.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 (421) hide show
  1. package/README.md +5 -4
  2. package/dist/{schema/Schema.d.ts → Schema.d.ts} +17 -27
  3. package/dist/Schema.d.ts.map +1 -0
  4. package/dist/{schema/Schema.js → Schema.js} +21 -61
  5. package/dist/Schema.js.map +1 -0
  6. package/dist/SchemaDefinition.d.ts +73 -0
  7. package/dist/SchemaDefinition.d.ts.map +1 -0
  8. package/dist/SchemaDefinition.js +184 -0
  9. package/dist/SchemaDefinition.js.map +1 -0
  10. package/dist/SchemaDerivation.d.ts +16 -0
  11. package/dist/SchemaDerivation.d.ts.map +1 -0
  12. package/dist/SchemaDerivation.js +31 -0
  13. package/dist/SchemaDerivation.js.map +1 -0
  14. package/dist/{schema/SchemaView.d.ts → SchemaView.d.ts} +9 -9
  15. package/dist/SchemaView.d.ts.map +1 -0
  16. package/dist/{schema/SchemaView.js.map → SchemaView.js.map} +1 -1
  17. package/dist/{schema/axis → axis}/Axis.d.ts +2 -2
  18. package/dist/axis/Axis.d.ts.map +1 -0
  19. package/dist/axis/Axis.js.map +1 -0
  20. package/dist/axis/Meta.d.ts.map +1 -0
  21. package/dist/{schema/axis → axis}/Meta.js.map +1 -1
  22. package/dist/axis/boundary/Boundaries.d.ts +21 -0
  23. package/dist/axis/boundary/Boundaries.d.ts.map +1 -0
  24. package/dist/axis/boundary/Boundaries.js +25 -0
  25. package/dist/axis/boundary/Boundaries.js.map +1 -0
  26. package/dist/{schema/axis → axis}/boundary/Boundary.d.ts +3 -1
  27. package/dist/axis/boundary/Boundary.d.ts.map +1 -0
  28. package/dist/{schema/axis → axis}/boundary/Boundary.js +8 -6
  29. package/dist/axis/boundary/Boundary.js.map +1 -0
  30. package/dist/axis/boundary/BoundaryAxis.d.ts.map +1 -0
  31. package/dist/{schema/axis → axis}/boundary/BoundaryAxis.js +4 -4
  32. package/dist/axis/boundary/BoundaryAxis.js.map +1 -0
  33. package/dist/{schema/axis → axis}/lifecycle/Clock.d.ts +1 -1
  34. package/dist/axis/lifecycle/Clock.d.ts.map +1 -0
  35. package/dist/{schema/axis → axis}/lifecycle/Clock.js +1 -1
  36. package/dist/axis/lifecycle/Clock.js.map +1 -0
  37. package/dist/axis/lifecycle/Generators.d.ts +10 -0
  38. package/dist/axis/lifecycle/Generators.d.ts.map +1 -0
  39. package/dist/axis/lifecycle/Generators.js +20 -0
  40. package/dist/axis/lifecycle/Generators.js.map +1 -0
  41. package/dist/axis/lifecycle/Lifecycle.d.ts.map +1 -0
  42. package/dist/axis/lifecycle/Lifecycle.js.map +1 -0
  43. package/dist/axis/lifecycle/LifecycleAxis.d.ts.map +1 -0
  44. package/dist/{schema/axis → axis}/lifecycle/LifecycleAxis.js +11 -6
  45. package/dist/axis/lifecycle/LifecycleAxis.js.map +1 -0
  46. package/dist/axis/lifecycle/apply.d.ts +15 -0
  47. package/dist/axis/lifecycle/apply.d.ts.map +1 -0
  48. package/dist/axis/lifecycle/apply.js +50 -0
  49. package/dist/axis/lifecycle/apply.js.map +1 -0
  50. package/dist/{schema/axis → axis}/role/Relation.d.ts +3 -2
  51. package/dist/axis/role/Relation.d.ts.map +1 -0
  52. package/dist/{schema/axis → axis}/role/Relation.js +6 -5
  53. package/dist/axis/role/Relation.js.map +1 -0
  54. package/dist/{schema/axis → axis}/role/Role.d.ts +6 -6
  55. package/dist/axis/role/Role.d.ts.map +1 -0
  56. package/dist/axis/role/Role.js +36 -0
  57. package/dist/axis/role/Role.js.map +1 -0
  58. package/dist/{schema/axis → axis}/role/RoleAxis.d.ts +1 -1
  59. package/dist/axis/role/RoleAxis.d.ts.map +1 -0
  60. package/dist/{schema/axis → axis}/role/RoleAxis.js +28 -36
  61. package/dist/axis/role/RoleAxis.js.map +1 -0
  62. package/dist/axis/shape/Formats.d.ts +11 -0
  63. package/dist/axis/shape/Formats.d.ts.map +1 -0
  64. package/dist/axis/shape/Formats.js +9 -0
  65. package/dist/axis/shape/Formats.js.map +1 -0
  66. package/dist/{schema/axis → axis}/shape/Shape.d.ts +5 -3
  67. package/dist/axis/shape/Shape.d.ts.map +1 -0
  68. package/dist/{schema/axis → axis}/shape/Shape.js +13 -13
  69. package/dist/axis/shape/Shape.js.map +1 -0
  70. package/dist/entity/EntityAdapterSet.d.ts +22 -0
  71. package/dist/entity/EntityAdapterSet.d.ts.map +1 -0
  72. package/dist/entity/EntityAdapterSet.js +63 -0
  73. package/dist/entity/EntityAdapterSet.js.map +1 -0
  74. package/dist/entity/EntityAdapters.d.ts +1 -24
  75. package/dist/entity/EntityAdapters.d.ts.map +1 -1
  76. package/dist/entity/EntityAdapters.js +1 -74
  77. package/dist/entity/EntityAdapters.js.map +1 -1
  78. package/dist/entity/EntityDeclarations.d.ts +1 -1
  79. package/dist/entity/EntityDeclarations.d.ts.map +1 -1
  80. package/dist/entity.d.ts +7 -2
  81. package/dist/entity.d.ts.map +1 -1
  82. package/dist/entity.js +6 -1
  83. package/dist/entity.js.map +1 -1
  84. package/dist/{schema/fields → field}/Field.d.ts +2 -3
  85. package/dist/field/Field.d.ts.map +1 -0
  86. package/dist/field/Field.js +35 -0
  87. package/dist/field/Field.js.map +1 -0
  88. package/dist/field/FieldSet.d.ts +22 -0
  89. package/dist/field/FieldSet.d.ts.map +1 -0
  90. package/dist/field/FieldSet.js +58 -0
  91. package/dist/field/FieldSet.js.map +1 -0
  92. package/dist/index.d.ts +27 -30
  93. package/dist/index.d.ts.map +1 -1
  94. package/dist/index.js +20 -23
  95. package/dist/index.js.map +1 -1
  96. package/dist/lib/Registry.d.ts +12 -0
  97. package/dist/lib/Registry.d.ts.map +1 -0
  98. package/dist/lib/Registry.js +29 -0
  99. package/dist/lib/Registry.js.map +1 -0
  100. package/dist/lib/utils.d.ts.map +1 -0
  101. package/dist/lib/utils.js.map +1 -0
  102. package/dist/projection/Cases.d.ts +9 -20
  103. package/dist/projection/Cases.d.ts.map +1 -1
  104. package/dist/projection/Cases.js +44 -39
  105. package/dist/projection/Cases.js.map +1 -1
  106. package/dist/projection/Visibility.d.ts +2 -1
  107. package/dist/projection/Visibility.d.ts.map +1 -1
  108. package/dist/projection/Visibility.js +4 -3
  109. package/dist/projection/Visibility.js.map +1 -1
  110. package/dist/projection/card/Bundle.d.ts +1 -1
  111. package/dist/projection/card/Bundle.d.ts.map +1 -1
  112. package/dist/projection/card/Bundle.js +7 -7
  113. package/dist/projection/card/Bundle.js.map +1 -1
  114. package/dist/projection/card/Card.d.ts +7 -13
  115. package/dist/projection/card/Card.d.ts.map +1 -1
  116. package/dist/projection/card/Card.js +96 -69
  117. package/dist/projection/card/Card.js.map +1 -1
  118. package/dist/projection/card/Descriptor.d.ts +7 -7
  119. package/dist/projection/card/Descriptor.d.ts.map +1 -1
  120. package/dist/projection/card/admission.d.ts +15 -5
  121. package/dist/projection/card/admission.d.ts.map +1 -1
  122. package/dist/projection/card/admission.js +15 -5
  123. package/dist/projection/card/admission.js.map +1 -1
  124. package/dist/projection/card/diff.d.ts +2 -2
  125. package/dist/projection/card/diff.d.ts.map +1 -1
  126. package/dist/{judge/result.d.ts → validation.d.ts} +1 -1
  127. package/dist/validation.d.ts.map +1 -0
  128. package/dist/validation.js +2 -0
  129. package/dist/validation.js.map +1 -0
  130. package/dist/validator/AdapterFieldValidator.d.ts +14 -0
  131. package/dist/validator/AdapterFieldValidator.d.ts.map +1 -0
  132. package/dist/validator/AdapterFieldValidator.js +36 -0
  133. package/dist/validator/AdapterFieldValidator.js.map +1 -0
  134. package/dist/validator/FieldDeclarationValidator.d.ts +9 -0
  135. package/dist/validator/FieldDeclarationValidator.d.ts.map +1 -0
  136. package/dist/{judge/FieldJudge.js → validator/FieldDeclarationValidator.js} +8 -8
  137. package/dist/validator/FieldDeclarationValidator.js.map +1 -0
  138. package/dist/validator/FieldValueValidator.d.ts +13 -0
  139. package/dist/validator/FieldValueValidator.d.ts.map +1 -0
  140. package/dist/{judge/ValueJudge.js → validator/FieldValueValidator.js} +12 -11
  141. package/dist/validator/FieldValueValidator.js.map +1 -0
  142. package/dist/{judge/RowRefusal.d.ts → validator/InputRefusal.d.ts} +3 -4
  143. package/dist/validator/InputRefusal.d.ts.map +1 -0
  144. package/dist/{judge/RowRefusal.js → validator/InputRefusal.js} +3 -3
  145. package/dist/validator/InputRefusal.js.map +1 -0
  146. package/dist/validator/InputValidator.d.ts +15 -0
  147. package/dist/validator/InputValidator.d.ts.map +1 -0
  148. package/dist/{judge/RowJudge.js → validator/InputValidator.js} +24 -20
  149. package/dist/validator/InputValidator.js.map +1 -0
  150. package/dist/vocabulary/bool.d.ts +6 -1
  151. package/dist/vocabulary/bool.d.ts.map +1 -1
  152. package/dist/vocabulary/bool.js +6 -1
  153. package/dist/vocabulary/bool.js.map +1 -1
  154. package/dist/vocabulary/created.d.ts +6 -1
  155. package/dist/vocabulary/created.d.ts.map +1 -1
  156. package/dist/vocabulary/created.js +5 -0
  157. package/dist/vocabulary/created.js.map +1 -1
  158. package/dist/vocabulary/date.d.ts +6 -1
  159. package/dist/vocabulary/date.d.ts.map +1 -1
  160. package/dist/vocabulary/date.js +6 -1
  161. package/dist/vocabulary/date.js.map +1 -1
  162. package/dist/vocabulary/email.d.ts +6 -1
  163. package/dist/vocabulary/email.d.ts.map +1 -1
  164. package/dist/vocabulary/email.js +5 -0
  165. package/dist/vocabulary/email.js.map +1 -1
  166. package/dist/vocabulary/immutable.d.ts +6 -1
  167. package/dist/vocabulary/immutable.d.ts.map +1 -1
  168. package/dist/vocabulary/immutable.js +5 -0
  169. package/dist/vocabulary/immutable.js.map +1 -1
  170. package/dist/vocabulary/indexed.d.ts +6 -1
  171. package/dist/vocabulary/indexed.d.ts.map +1 -1
  172. package/dist/vocabulary/indexed.js +5 -0
  173. package/dist/vocabulary/indexed.js.map +1 -1
  174. package/dist/vocabulary/json.d.ts +2 -2
  175. package/dist/vocabulary/json.d.ts.map +1 -1
  176. package/dist/vocabulary/json.js +9 -4
  177. package/dist/vocabulary/json.js.map +1 -1
  178. package/dist/vocabulary/list.d.ts +6 -1
  179. package/dist/vocabulary/list.d.ts.map +1 -1
  180. package/dist/vocabulary/list.js +6 -1
  181. package/dist/vocabulary/list.js.map +1 -1
  182. package/dist/vocabulary/many.d.ts +7 -2
  183. package/dist/vocabulary/many.d.ts.map +1 -1
  184. package/dist/vocabulary/many.js +7 -2
  185. package/dist/vocabulary/many.js.map +1 -1
  186. package/dist/vocabulary/nullable.d.ts +6 -1
  187. package/dist/vocabulary/nullable.d.ts.map +1 -1
  188. package/dist/vocabulary/nullable.js +7 -2
  189. package/dist/vocabulary/nullable.js.map +1 -1
  190. package/dist/vocabulary/number.d.ts +6 -1
  191. package/dist/vocabulary/number.d.ts.map +1 -1
  192. package/dist/vocabulary/number.js +6 -1
  193. package/dist/vocabulary/number.js.map +1 -1
  194. package/dist/vocabulary/oneOf.d.ts +1 -1
  195. package/dist/vocabulary/oneOf.d.ts.map +1 -1
  196. package/dist/vocabulary/oneOf.js +6 -1
  197. package/dist/vocabulary/oneOf.js.map +1 -1
  198. package/dist/vocabulary/optional.d.ts +6 -1
  199. package/dist/vocabulary/optional.d.ts.map +1 -1
  200. package/dist/vocabulary/optional.js +7 -2
  201. package/dist/vocabulary/optional.js.map +1 -1
  202. package/dist/vocabulary/primary.d.ts +2 -2
  203. package/dist/vocabulary/primary.d.ts.map +1 -1
  204. package/dist/vocabulary/primary.js +7 -2
  205. package/dist/vocabulary/primary.js.map +1 -1
  206. package/dist/vocabulary/readOnly.d.ts +6 -1
  207. package/dist/vocabulary/readOnly.d.ts.map +1 -1
  208. package/dist/vocabulary/readOnly.js +6 -1
  209. package/dist/vocabulary/readOnly.js.map +1 -1
  210. package/dist/vocabulary/ref.d.ts +7 -2
  211. package/dist/vocabulary/ref.d.ts.map +1 -1
  212. package/dist/vocabulary/ref.js +7 -2
  213. package/dist/vocabulary/ref.js.map +1 -1
  214. package/dist/vocabulary/text.d.ts +7 -2
  215. package/dist/vocabulary/text.d.ts.map +1 -1
  216. package/dist/vocabulary/text.js +6 -1
  217. package/dist/vocabulary/text.js.map +1 -1
  218. package/dist/vocabulary/unique.d.ts +6 -1
  219. package/dist/vocabulary/unique.d.ts.map +1 -1
  220. package/dist/vocabulary/unique.js +7 -3
  221. package/dist/vocabulary/unique.js.map +1 -1
  222. package/dist/vocabulary/updated.d.ts +6 -1
  223. package/dist/vocabulary/updated.d.ts.map +1 -1
  224. package/dist/vocabulary/updated.js +5 -0
  225. package/dist/vocabulary/updated.js.map +1 -1
  226. package/dist/vocabulary/url.d.ts +6 -1
  227. package/dist/vocabulary/url.d.ts.map +1 -1
  228. package/dist/vocabulary/url.js +5 -0
  229. package/dist/vocabulary/url.js.map +1 -1
  230. package/dist/vocabulary/vocabulary.d.ts +6 -1
  231. package/dist/vocabulary/vocabulary.d.ts.map +1 -1
  232. package/dist/vocabulary/vocabulary.js +22 -12
  233. package/dist/vocabulary/vocabulary.js.map +1 -1
  234. package/dist/vocabulary/writeOnly.d.ts +6 -1
  235. package/dist/vocabulary/writeOnly.d.ts.map +1 -1
  236. package/dist/vocabulary/writeOnly.js +6 -1
  237. package/dist/vocabulary/writeOnly.js.map +1 -1
  238. package/package.json +1 -1
  239. package/src/{schema/Schema.ts → Schema.ts} +31 -74
  240. package/src/SchemaDefinition.ts +257 -0
  241. package/src/SchemaDerivation.ts +44 -0
  242. package/src/{schema/SchemaView.ts → SchemaView.ts} +9 -9
  243. package/src/{schema/axis → axis}/Axis.ts +2 -2
  244. package/src/axis/boundary/Boundaries.ts +37 -0
  245. package/src/{schema/axis → axis}/boundary/Boundary.ts +9 -7
  246. package/src/{schema/axis → axis}/boundary/BoundaryAxis.ts +4 -4
  247. package/src/{schema/axis → axis}/lifecycle/Clock.ts +1 -1
  248. package/src/axis/lifecycle/Generators.ts +29 -0
  249. package/src/{schema/axis → axis}/lifecycle/LifecycleAxis.ts +12 -7
  250. package/src/axis/lifecycle/apply.ts +53 -0
  251. package/src/{schema/axis → axis}/role/Relation.ts +6 -5
  252. package/src/axis/role/Role.ts +52 -0
  253. package/src/{schema/axis → axis}/role/RoleAxis.ts +28 -40
  254. package/src/axis/shape/Formats.ts +20 -0
  255. package/src/{schema/axis → axis}/shape/Shape.ts +18 -18
  256. package/src/entity/EntityAdapterSet.ts +81 -0
  257. package/src/entity/EntityAdapters.ts +4 -96
  258. package/src/entity/EntityDeclarations.ts +1 -1
  259. package/src/entity.ts +7 -2
  260. package/src/{schema/fields → field}/Field.ts +8 -26
  261. package/src/field/FieldSet.ts +75 -0
  262. package/src/index.ts +29 -44
  263. package/src/lib/Registry.ts +37 -0
  264. package/src/projection/Cases.ts +56 -55
  265. package/src/projection/Visibility.ts +5 -4
  266. package/src/projection/card/Bundle.ts +9 -9
  267. package/src/projection/card/Card.ts +101 -74
  268. package/src/projection/card/Descriptor.ts +7 -7
  269. package/src/projection/card/admission.ts +16 -6
  270. package/src/projection/card/diff.ts +2 -2
  271. package/src/validator/AdapterFieldValidator.ts +40 -0
  272. package/src/{judge/FieldJudge.ts → validator/FieldDeclarationValidator.ts} +10 -10
  273. package/src/{judge/ValueJudge.ts → validator/FieldValueValidator.ts} +14 -13
  274. package/src/{judge/RowRefusal.ts → validator/InputRefusal.ts} +2 -4
  275. package/src/{judge/RowJudge.ts → validator/InputValidator.ts} +30 -23
  276. package/src/vocabulary/bool.ts +6 -1
  277. package/src/vocabulary/created.ts +6 -1
  278. package/src/vocabulary/date.ts +6 -1
  279. package/src/vocabulary/email.ts +6 -1
  280. package/src/vocabulary/immutable.ts +6 -1
  281. package/src/vocabulary/indexed.ts +6 -1
  282. package/src/vocabulary/json.ts +10 -5
  283. package/src/vocabulary/list.ts +6 -1
  284. package/src/vocabulary/many.ts +7 -2
  285. package/src/vocabulary/nullable.ts +8 -3
  286. package/src/vocabulary/number.ts +6 -1
  287. package/src/vocabulary/oneOf.ts +6 -1
  288. package/src/vocabulary/optional.ts +8 -3
  289. package/src/vocabulary/primary.ts +7 -2
  290. package/src/vocabulary/readOnly.ts +7 -2
  291. package/src/vocabulary/ref.ts +7 -2
  292. package/src/vocabulary/text.ts +7 -2
  293. package/src/vocabulary/unique.ts +8 -4
  294. package/src/vocabulary/updated.ts +6 -1
  295. package/src/vocabulary/url.ts +6 -1
  296. package/src/vocabulary/vocabulary.ts +22 -12
  297. package/src/vocabulary/writeOnly.ts +7 -2
  298. package/dist/judge/FieldJudge.d.ts +0 -9
  299. package/dist/judge/FieldJudge.d.ts.map +0 -1
  300. package/dist/judge/FieldJudge.js.map +0 -1
  301. package/dist/judge/RowJudge.d.ts +0 -12
  302. package/dist/judge/RowJudge.d.ts.map +0 -1
  303. package/dist/judge/RowJudge.js.map +0 -1
  304. package/dist/judge/RowRefusal.d.ts.map +0 -1
  305. package/dist/judge/RowRefusal.js.map +0 -1
  306. package/dist/judge/ValueJudge.d.ts +0 -12
  307. package/dist/judge/ValueJudge.d.ts.map +0 -1
  308. package/dist/judge/ValueJudge.js.map +0 -1
  309. package/dist/judge/options.d.ts +0 -4
  310. package/dist/judge/options.d.ts.map +0 -1
  311. package/dist/judge/options.js +0 -2
  312. package/dist/judge/options.js.map +0 -1
  313. package/dist/judge/result.d.ts.map +0 -1
  314. package/dist/judge/result.js +0 -2
  315. package/dist/judge/result.js.map +0 -1
  316. package/dist/projection/SchemaOrCard.d.ts +0 -7
  317. package/dist/projection/SchemaOrCard.d.ts.map +0 -1
  318. package/dist/projection/SchemaOrCard.js +0 -11
  319. package/dist/projection/SchemaOrCard.js.map +0 -1
  320. package/dist/projection/card/EntityTypeSource.d.ts +0 -12
  321. package/dist/projection/card/EntityTypeSource.d.ts.map +0 -1
  322. package/dist/projection/card/EntityTypeSource.js +0 -78
  323. package/dist/projection/card/EntityTypeSource.js.map +0 -1
  324. package/dist/projection/card/FacadeTypeSource.d.ts +0 -19
  325. package/dist/projection/card/FacadeTypeSource.d.ts.map +0 -1
  326. package/dist/projection/card/FacadeTypeSource.js +0 -40
  327. package/dist/projection/card/FacadeTypeSource.js.map +0 -1
  328. package/dist/schema/Schema.d.ts.map +0 -1
  329. package/dist/schema/Schema.js.map +0 -1
  330. package/dist/schema/SchemaDefinition.d.ts +0 -60
  331. package/dist/schema/SchemaDefinition.d.ts.map +0 -1
  332. package/dist/schema/SchemaDefinition.js +0 -85
  333. package/dist/schema/SchemaDefinition.js.map +0 -1
  334. package/dist/schema/SchemaDerivation.d.ts +0 -34
  335. package/dist/schema/SchemaDerivation.d.ts.map +0 -1
  336. package/dist/schema/SchemaDerivation.js +0 -51
  337. package/dist/schema/SchemaDerivation.js.map +0 -1
  338. package/dist/schema/SchemaView.d.ts.map +0 -1
  339. package/dist/schema/axis/Axis.d.ts.map +0 -1
  340. package/dist/schema/axis/Axis.js.map +0 -1
  341. package/dist/schema/axis/Meta.d.ts.map +0 -1
  342. package/dist/schema/axis/boundary/Boundaries.d.ts +0 -20
  343. package/dist/schema/axis/boundary/Boundaries.d.ts.map +0 -1
  344. package/dist/schema/axis/boundary/Boundaries.js +0 -31
  345. package/dist/schema/axis/boundary/Boundaries.js.map +0 -1
  346. package/dist/schema/axis/boundary/Boundary.d.ts.map +0 -1
  347. package/dist/schema/axis/boundary/Boundary.js.map +0 -1
  348. package/dist/schema/axis/boundary/BoundaryAxis.d.ts.map +0 -1
  349. package/dist/schema/axis/boundary/BoundaryAxis.js.map +0 -1
  350. package/dist/schema/axis/lifecycle/Clock.d.ts.map +0 -1
  351. package/dist/schema/axis/lifecycle/Clock.js.map +0 -1
  352. package/dist/schema/axis/lifecycle/Generators.d.ts +0 -18
  353. package/dist/schema/axis/lifecycle/Generators.d.ts.map +0 -1
  354. package/dist/schema/axis/lifecycle/Generators.js +0 -38
  355. package/dist/schema/axis/lifecycle/Generators.js.map +0 -1
  356. package/dist/schema/axis/lifecycle/Lifecycle.d.ts.map +0 -1
  357. package/dist/schema/axis/lifecycle/Lifecycle.js.map +0 -1
  358. package/dist/schema/axis/lifecycle/LifecycleAxis.d.ts.map +0 -1
  359. package/dist/schema/axis/lifecycle/LifecycleAxis.js.map +0 -1
  360. package/dist/schema/axis/lifecycle/apply.d.ts +0 -6
  361. package/dist/schema/axis/lifecycle/apply.d.ts.map +0 -1
  362. package/dist/schema/axis/lifecycle/apply.js +0 -34
  363. package/dist/schema/axis/lifecycle/apply.js.map +0 -1
  364. package/dist/schema/axis/role/Relation.d.ts.map +0 -1
  365. package/dist/schema/axis/role/Relation.js.map +0 -1
  366. package/dist/schema/axis/role/Role.d.ts.map +0 -1
  367. package/dist/schema/axis/role/Role.js +0 -40
  368. package/dist/schema/axis/role/Role.js.map +0 -1
  369. package/dist/schema/axis/role/RoleAxis.d.ts.map +0 -1
  370. package/dist/schema/axis/role/RoleAxis.js.map +0 -1
  371. package/dist/schema/axis/shape/Formats.d.ts +0 -8
  372. package/dist/schema/axis/shape/Formats.d.ts.map +0 -1
  373. package/dist/schema/axis/shape/Formats.js +0 -10
  374. package/dist/schema/axis/shape/Formats.js.map +0 -1
  375. package/dist/schema/axis/shape/Shape.d.ts.map +0 -1
  376. package/dist/schema/axis/shape/Shape.js.map +0 -1
  377. package/dist/schema/fields/Field.d.ts.map +0 -1
  378. package/dist/schema/fields/Field.js +0 -52
  379. package/dist/schema/fields/Field.js.map +0 -1
  380. package/dist/schema/fields/FieldSet.d.ts +0 -28
  381. package/dist/schema/fields/FieldSet.d.ts.map +0 -1
  382. package/dist/schema/fields/FieldSet.js +0 -55
  383. package/dist/schema/fields/FieldSet.js.map +0 -1
  384. package/dist/schema/fields/constraint/FieldGroup.d.ts +0 -21
  385. package/dist/schema/fields/constraint/FieldGroup.d.ts.map +0 -1
  386. package/dist/schema/fields/constraint/FieldGroup.js +0 -65
  387. package/dist/schema/fields/constraint/FieldGroup.js.map +0 -1
  388. package/dist/schema/fields/constraint/Unique.d.ts +0 -4
  389. package/dist/schema/fields/constraint/Unique.d.ts.map +0 -1
  390. package/dist/schema/fields/constraint/Unique.js +0 -4
  391. package/dist/schema/fields/constraint/Unique.js.map +0 -1
  392. package/dist/utils.d.ts.map +0 -1
  393. package/dist/utils.js.map +0 -1
  394. package/src/judge/options.ts +0 -3
  395. package/src/projection/SchemaOrCard.ts +0 -18
  396. package/src/projection/card/EntityTypeSource.ts +0 -90
  397. package/src/projection/card/FacadeTypeSource.ts +0 -55
  398. package/src/schema/SchemaDefinition.ts +0 -150
  399. package/src/schema/SchemaDerivation.ts +0 -65
  400. package/src/schema/axis/boundary/Boundaries.ts +0 -44
  401. package/src/schema/axis/lifecycle/Generators.ts +0 -46
  402. package/src/schema/axis/lifecycle/apply.ts +0 -39
  403. package/src/schema/axis/role/Role.ts +0 -56
  404. package/src/schema/axis/shape/Formats.ts +0 -22
  405. package/src/schema/fields/FieldSet.ts +0 -63
  406. package/src/schema/fields/constraint/FieldGroup.ts +0 -84
  407. package/src/schema/fields/constraint/Unique.ts +0 -3
  408. /package/dist/{schema/SchemaView.js → SchemaView.js} +0 -0
  409. /package/dist/{schema/axis → axis}/Axis.js +0 -0
  410. /package/dist/{schema/axis → axis}/Meta.d.ts +0 -0
  411. /package/dist/{schema/axis → axis}/Meta.js +0 -0
  412. /package/dist/{schema/axis → axis}/boundary/BoundaryAxis.d.ts +0 -0
  413. /package/dist/{schema/axis → axis}/lifecycle/Lifecycle.d.ts +0 -0
  414. /package/dist/{schema/axis → axis}/lifecycle/Lifecycle.js +0 -0
  415. /package/dist/{schema/axis → axis}/lifecycle/LifecycleAxis.d.ts +0 -0
  416. /package/dist/{utils.d.ts → lib/utils.d.ts} +0 -0
  417. /package/dist/{utils.js → lib/utils.js} +0 -0
  418. /package/src/{schema/axis → axis}/Meta.ts +0 -0
  419. /package/src/{schema/axis → axis}/lifecycle/Lifecycle.ts +0 -0
  420. /package/src/{utils.ts → lib/utils.ts} +0 -0
  421. /package/src/{judge/result.ts → validation.ts} +0 -0
@@ -1,12 +1,12 @@
1
1
  import { roleAxis } from './role/RoleAxis.js';
2
2
  import { lifecycleAxis } from './lifecycle/LifecycleAxis.js';
3
3
  import { boundaryAxis } from './boundary/BoundaryAxis.js';
4
- import type { ValidationError } from '../../judge/result.js';
4
+ import type { ValidationError } from '../validation.js';
5
5
 
6
6
  export interface Axis<Declared = unknown, Wire = unknown> {
7
7
  readonly slot: 'role' | 'lifecycle' | 'boundary';
8
8
 
9
- judge(value: unknown, errors: ValidationError[]): void;
9
+ validator(value: unknown, errors: ValidationError[]): void;
10
10
 
11
11
  describe(value: Declared, key: string): Wire | undefined;
12
12
 
@@ -0,0 +1,37 @@
1
+ import type { BoundaryRules } from './Boundary.js';
2
+ import { Registry } from '../../lib/Registry.js';
3
+
4
+ export type Decoder = (value: unknown) => { value: unknown } | { error: string };
5
+ export type Encoder = (value: unknown) => unknown;
6
+
7
+ /**
8
+ * `decoders` for a value coming in, `encoders` for one going out, `aliases` for the word
9
+ * that names a pair. Three registries, so a codec is never half declared.
10
+ * FR : `decoders` à l'entrée, `encoders` à la sortie, `aliases` pour le mot qui nomme la paire.
11
+ * `Boundaries.aliases.register('isoDate', { in: { decode: 'isoDate' }, out: { encode: 'isoDate' } })`
12
+ * → a field saying `boundary: 'isoDate'` decodes to a `Date` and leaves as a string
13
+ */
14
+ export const Boundaries = {
15
+ decoders: new Registry<Decoder>(
16
+ 'boundary decoder',
17
+ 'call Boundaries.decoders.register(name, fn)',
18
+ ),
19
+ encoders: new Registry<Encoder>(
20
+ 'boundary encoder',
21
+ 'call Boundaries.encoders.register(name, fn)',
22
+ ),
23
+ aliases: new Registry<BoundaryRules>('boundary alias'),
24
+ };
25
+
26
+ Boundaries.decoders.register('isoDate', (value) => {
27
+ if (value instanceof Date) return { value };
28
+ if (typeof value === 'string') {
29
+ const date = new Date(value);
30
+ return Number.isNaN(date.getTime()) ? { error: 'Invalid date' } : { value: date };
31
+ }
32
+ return { error: 'Expected a date' };
33
+ });
34
+ Boundaries.encoders.register('isoDate', (value) =>
35
+ value instanceof Date ? value.toISOString() : value,
36
+ );
37
+ Boundaries.aliases.register('isoDate', { in: { decode: 'isoDate' }, out: { encode: 'isoDate' } });
@@ -1,7 +1,7 @@
1
1
  import type { Decoder, Encoder } from './Boundaries.js';
2
2
  import { Boundaries } from './Boundaries.js';
3
- import type { Field } from '../../fields/Field.js';
4
- import { Anatomy, type Shape } from '../shape/Shape.js';
3
+ import type { Field } from '../../field/Field.js';
4
+ import { Shapes, type Shape } from '../shape/Shape.js';
5
5
 
6
6
  export interface BoundaryRules {
7
7
  in?: 'closed' | { decode: string };
@@ -29,12 +29,13 @@ export class Boundary implements BoundaryRules {
29
29
  this.encode = codecs?.encode ?? identityEncoder;
30
30
  }
31
31
 
32
+ /** An unknown alias stops here. */
32
33
  static declared(field: Field): Boundary {
33
34
  const ref = field.boundary;
34
35
  if (ref === undefined) return new Boundary();
35
36
  if (typeof ref !== 'string') return new Boundary(ref);
36
37
 
37
- const alias = Boundaries.alias(ref);
38
+ const alias = Boundaries.aliases.find(ref);
38
39
  if (!alias) throw new Error(`Unknown boundary alias: '${ref}'`);
39
40
  return new Boundary(alias);
40
41
  }
@@ -49,19 +50,20 @@ export class Boundary implements BoundaryRules {
49
50
  return new Boundary(rules, {
50
51
  decode:
51
52
  typeof rules.in === 'object'
52
- ? Boundaries.decoder(rules.in.decode)
53
+ ? Boundaries.decoders.resolve(rules.in.decode)
53
54
  : identityDecoder,
54
55
  encode:
55
56
  typeof rules.out === 'object'
56
- ? Boundaries.encoder(rules.out.encode)
57
+ ? Boundaries.encoders.resolve(rules.out.encode)
57
58
  : identityEncoder,
58
59
  });
59
60
  }
60
61
 
62
+ /** `date-time` means a `Date` on both sides, without a word in the entity. */
61
63
  static forShape(shape: Shape | undefined): Boundary {
62
- const base = Anatomy.of(shape).base;
64
+ const base = Shapes.of(shape).base;
63
65
  if (base?.type === 'string' && base.format === 'date-time') {
64
- return new Boundary(Boundaries.alias('isoDate')!);
66
+ return new Boundary(Boundaries.aliases.find('isoDate')!);
65
67
  }
66
68
  return new Boundary();
67
69
  }
@@ -1,12 +1,12 @@
1
1
  import type { Axis } from '../Axis.js';
2
- import { admitWire } from '../../../projection/card/admission.js';
3
- import { isObject } from '../../../utils.js';
2
+ import { admitWire } from '../../projection/card/admission.js';
3
+ import { isObject } from '../../lib/utils.js';
4
4
  import type { BoundaryRef } from './Boundary.js';
5
5
 
6
6
  export const boundaryAxis: Axis<BoundaryRef, BoundaryRef> = {
7
7
  slot: 'boundary',
8
8
 
9
- judge(value, errors) {
9
+ validator(value, errors) {
10
10
  if (typeof value === 'string') return;
11
11
  if (!isObject(value)) {
12
12
  errors.push({
@@ -26,7 +26,7 @@ export const boundaryAxis: Axis<BoundaryRef, BoundaryRef> = {
26
26
 
27
27
  describe: (value) => value,
28
28
  reconstruct: (wire) => {
29
- admitWire(boundaryAxis.judge, wire, 'boundary');
29
+ admitWire(boundaryAxis.validator, wire, 'boundary');
30
30
  return wire;
31
31
  },
32
32
  };
@@ -16,7 +16,7 @@ export class Clock {
16
16
  return this.reading();
17
17
  }
18
18
 
19
- /** Freeze the instant this clock reads. Returns the gesture that restores it. */
19
+ /** One substitution moves time for every stamp in the process at once. */
20
20
  static freeze(at: number | Date): () => void {
21
21
  const previous = this.reading;
22
22
  const instant = at instanceof Date ? at.getTime() : at;
@@ -0,0 +1,29 @@
1
+ import { createId } from '@paralleldrive/cuid2';
2
+
3
+ import { Registry } from '../../lib/Registry.js';
4
+
5
+ export type GeneratorRef = 'cuid2' | 'uuid' | 'nanoid' | (string & {});
6
+
7
+ const NANOID_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
8
+
9
+ function nanoid(): string {
10
+ const bytes = new Uint8Array(21);
11
+ globalThis.crypto.getRandomValues(bytes);
12
+ return Array.from(bytes, (byte) => NANOID_ALPHABET[byte & 63]).join('');
13
+ }
14
+
15
+ /**
16
+ * Who answers a `generate:` name. `cuid2`, `uuid` and `nanoid` are registered like any
17
+ * other, so `Generators.resolve('ulid')` is refused here and lists the three.
18
+ * FR : pour qu'un `generate:` soit répondu par un registre, builtins compris.
19
+ * `Generators.register('ulid', ulid)` → `create: { generate: 'ulid' }` now resolves
20
+ */
21
+ export const Generators = new Registry<() => string>(
22
+ 'generator',
23
+ 'call Generators.register(name, fn)',
24
+ [
25
+ ['cuid2', createId],
26
+ ['uuid', () => globalThis.crypto.randomUUID()],
27
+ ['nanoid', nanoid],
28
+ ],
29
+ );
@@ -1,18 +1,18 @@
1
1
  import type { Axis } from '../Axis.js';
2
- import type { ValidationError } from '../../../judge/result.js';
3
- import { admitWire, oneOfTokens } from '../../../projection/card/admission.js';
4
- import { isObject } from '../../../utils.js';
2
+ import type { ValidationError } from '../../validation.js';
3
+ import { admitWire, oneOfTokens } from '../../projection/card/admission.js';
4
+ import { isObject } from '../../lib/utils.js';
5
5
  import { CREATE_TOKENS, UPDATE_TOKENS, type LifecycleRules } from './Lifecycle.js';
6
6
 
7
7
  export const lifecycleAxis: Axis<LifecycleRules, LifecycleRules> = {
8
8
  slot: 'lifecycle',
9
9
 
10
- judge(value, errors) {
10
+ validator(value, errors) {
11
11
  if (!isObject(value)) {
12
12
  errors.push({ path: 'lifecycle', message: `Expected an object — got ${JSON.stringify(value)}` });
13
13
  return;
14
14
  }
15
- if (value.create !== undefined) judgeCreate(value.create, errors);
15
+ if (value.create !== undefined) validateCreate(value.create, errors);
16
16
  if (value.update !== undefined && !oneOfTokens(value.update, UPDATE_TOKENS)) {
17
17
  errors.push({
18
18
  path: 'lifecycle.update',
@@ -23,12 +23,17 @@ export const lifecycleAxis: Axis<LifecycleRules, LifecycleRules> = {
23
23
 
24
24
  describe: (value) => value,
25
25
  reconstruct: (wire) => {
26
- admitWire(lifecycleAxis.judge, wire, 'lifecycle');
26
+ admitWire(lifecycleAxis.validator, wire, 'lifecycle');
27
27
  return wire;
28
28
  },
29
29
  };
30
30
 
31
- function judgeCreate(rule: unknown, errors: ValidationError[]): void {
31
+ /**
32
+ * So the four legal ways to declare a creation are stated once, and anything else names them.
33
+ * FR : pour que les quatre façons de déclarer une création soient dites une fois.
34
+ * `create: 3` → `Expected 'now', 'optional', { value } or { generate } — got 3`
35
+ */
36
+ function validateCreate(rule: unknown, errors: ValidationError[]): void {
32
37
  if (oneOfTokens(rule, CREATE_TOKENS)) return;
33
38
  if (isObject(rule)) {
34
39
  if ('value' in rule) return;
@@ -0,0 +1,53 @@
1
+ import { Lifecycle } from './Lifecycle.js';
2
+ import { Generators } from './Generators.js';
3
+ import { Clock } from './Clock.js';
4
+ import { type Field, type Fields } from '../../field/Field.js';
5
+
6
+ /**
7
+ * So every storage realizes a create the same way.
8
+ * FR : pour que chaque storage réalise une création de la même façon.
9
+ * `applyCreate({ id: primary(), createdAt: created() }, { title: 'a' })`
10
+ * → `id` generated, `createdAt` stamped, `title` untouched
11
+ */
12
+ export function applyCreate(fields: Fields, input: Record<string, unknown>): Record<string, unknown> {
13
+ const values: Record<string, unknown> = { ...input };
14
+ const instant = Clock.now();
15
+
16
+ for (const [name, field] of Object.entries(fields) as [string, Field][]) {
17
+ if (name in values) continue;
18
+ const rule = Lifecycle.of(field);
19
+
20
+ if (rule.create === 'now') values[name] = new Date(instant);
21
+ else if (rule.literal) values[name] = freshValue(rule.literal.value);
22
+ else if (rule.generator) values[name] = Generators.resolve(rule.generator)();
23
+ }
24
+
25
+ return values;
26
+ }
27
+
28
+ /**
29
+ * So two rows created from one declared default never end up sharing the same object.
30
+ * FR : pour que deux lignes nées d'un même défaut ne partagent pas l'objet.
31
+ * `create: { value: [] }` → each instance gets its own array
32
+ */
33
+ function freshValue(value: unknown): unknown {
34
+ if (value === null || typeof value !== 'object') return value;
35
+ return structuredClone(value);
36
+ }
37
+
38
+ /**
39
+ * So `updated()` is stamped by the storage, and no handler has to remember it.
40
+ * FR : pour qu'`updated()` soit estampé par le storage, pas par le handler.
41
+ * `applyUpdate(fields, { title: 'b' })` → `updatedAt` added, nothing else
42
+ */
43
+ export function applyUpdate(fields: Fields, patch: Record<string, unknown>): Record<string, unknown> {
44
+ const values: Record<string, unknown> = { ...patch };
45
+ const instant = Clock.now();
46
+
47
+ for (const [name, field] of Object.entries(fields) as [string, Field][]) {
48
+ if (Lifecycle.of(field).stampedOnUpdate && !(name in values))
49
+ values[name] = new Date(instant);
50
+ }
51
+
52
+ return values;
53
+ }
@@ -10,22 +10,23 @@ export interface Relation {
10
10
  }
11
11
 
12
12
  /**
13
- * How a relation is built — the one place a target becomes a thunk.
13
+ * How a relation is built — the one place a target becomes `() => Entity`.
14
14
  *
15
15
  * A target arrives as the class or as a function returning it, because a cycle between two
16
16
  * entities can only be written the second way. Recognised by FORM, `getFields` on the
17
- * value: a class answers it, a thunk does not.
17
+ * value: a class answers it, `() => Post` does not.
18
18
  */
19
19
  export const Relation = {
20
+ /** `() => Post` so two entities can point at each other. */
20
21
  one(target: EntityConstructor | (() => EntityConstructor), cascade?: boolean): Relation {
21
- return { to: thunk(target), kind: 'one', onDelete: cascade ? 'cascade' : undefined };
22
+ return { to: normalizeTarget(target), kind: 'one', onDelete: cascade ? 'cascade' : undefined };
22
23
  },
23
24
 
24
25
  many(target: EntityConstructor | (() => EntityConstructor)): Relation {
25
- return { to: thunk(target), kind: 'many' };
26
+ return { to: normalizeTarget(target), kind: 'many' };
26
27
  },
27
28
  };
28
29
 
29
- function thunk<E extends EntityConstructor>(target: E | (() => E)): () => E {
30
+ function normalizeTarget<E extends EntityConstructor>(target: E | (() => E)): () => E {
30
31
  return 'getFields' in target ? () => target as E : (target as () => E);
31
32
  }
@@ -0,0 +1,52 @@
1
+ import type { EntityConstructor, Relation } from './Relation.js';
2
+
3
+ export interface RoleRules {
4
+ primary?: boolean;
5
+ index?: boolean;
6
+ unique?: boolean;
7
+ relation?: Relation;
8
+ }
9
+
10
+ export class Role implements RoleRules {
11
+ readonly primary?: boolean;
12
+ readonly index?: boolean;
13
+ readonly unique?: boolean;
14
+ readonly relation?: Relation;
15
+
16
+ private constructor(rules: RoleRules = {}) {
17
+ this.primary = rules.primary;
18
+ this.index = rules.index;
19
+ this.unique = rules.unique;
20
+ this.relation = rules.relation;
21
+ }
22
+
23
+ static of(field: { role?: RoleRules }): Role {
24
+ return new Role(field.role);
25
+ }
26
+
27
+ get isPrimary(): boolean {
28
+ return this.primary === true;
29
+ }
30
+
31
+ get isIndexed(): boolean {
32
+ return this.index === true;
33
+ }
34
+
35
+ /** A field unique on its own. A group spanning several belongs to the schema. */
36
+ get isUnique(): boolean {
37
+ return this.unique === true;
38
+ }
39
+
40
+ get isCollection(): boolean {
41
+ return this.relation?.kind === 'many';
42
+ }
43
+
44
+ get isReference(): boolean {
45
+ return this.relation?.kind === 'one';
46
+ }
47
+
48
+ /** Calls `() => Post` so no caller has to. */
49
+ get target(): EntityConstructor | undefined {
50
+ return this.relation?.to();
51
+ }
52
+ }
@@ -1,22 +1,20 @@
1
1
  import type { Axis, Resolver } from '../Axis.js';
2
- import { refuse, oneOfTokens } from '../../../projection/card/admission.js';
3
- import { isObject, lowerFirst } from '../../../utils.js';
4
- import type { ValidationError } from '../../../judge/result.js';
2
+ import { refuse, oneOfTokens } from '../../projection/card/admission.js';
3
+ import { isObject, lowerFirst } from '../../lib/utils.js';
4
+ import type { ValidationError } from '../../validation.js';
5
5
  import { ON_DELETE, RELATION_KINDS, type EntityConstructor, type Relation } from './Relation.js';
6
6
  import { type RoleRules } from './Role.js';
7
- import { FieldGroup } from '../../fields/constraint/FieldGroup.js';
8
- import { Unique } from '../../fields/constraint/Unique.js';
9
- import type { RoleDescriptor } from '../../../projection/card/Descriptor.js';
7
+ import type { RoleDescriptor } from '../../projection/card/Descriptor.js';
10
8
 
11
9
  export const roleAxis: Axis<RoleRules, RoleDescriptor> = {
12
10
  slot: 'role',
13
11
 
14
- judge(value, errors) {
12
+ validator(value, errors) {
15
13
  if (!isObject(value)) {
16
14
  errors.push({ path: 'role', message: `Expected an object — got ${JSON.stringify(value)}` });
17
15
  return;
18
16
  }
19
- for (const flag of ['primary', 'index'] as const) {
17
+ for (const flag of ['primary', 'index', 'unique'] as const) {
20
18
  if (value[flag] !== undefined && typeof value[flag] !== 'boolean') {
21
19
  errors.push({
22
20
  path: `role.${flag}`,
@@ -24,48 +22,33 @@ export const roleAxis: Axis<RoleRules, RoleDescriptor> = {
24
22
  });
25
23
  }
26
24
  }
27
- if (value.rules !== undefined) {
28
- const legal =
29
- Array.isArray(value.rules) &&
30
- value.rules.every(
31
- (rule) =>
32
- rule instanceof FieldGroup ||
33
- (Array.isArray(rule) && rule.every((member) => typeof member === 'string')),
34
- );
35
- if (!legal) {
36
- errors.push({
37
- path: 'role.rules',
38
- message: 'Expected field groups, or the member lists they denote — string[][]',
39
- });
40
- }
41
- }
42
- if (value.relation !== undefined) judgeRelation(value.relation, errors);
25
+ if (value.relation !== undefined) validateRelation(value.relation, errors);
43
26
  },
44
27
 
28
+ /** A card carries the target's NAME: a class cannot cross a process boundary. */
45
29
  describe(role, key) {
46
- const out: Mutable<RoleDescriptor> = {};
47
- if (role.primary) out.primary = true;
48
- const unique = (role.rules ?? []).filter((rule) => rule instanceof Unique);
49
- if (unique.length) out.unique = unique.map((rule) => [...rule.resolvedOn(key).members]);
50
- if (role.index) out.index = true;
30
+ const descriptor: Mutable<RoleDescriptor> = {};
31
+ if (role.primary) descriptor.primary = true;
32
+ if (role.unique) descriptor.unique = [[key]];
33
+ if (role.index) descriptor.index = true;
51
34
  if (role.relation) {
52
35
  const target = role.relation.to() as { name?: string };
53
- out.relation = {
36
+ descriptor.relation = {
54
37
  to: lowerFirst(target.name ?? ''),
55
38
  kind: role.relation.kind,
56
39
  ...(role.relation.onDelete ? { onDelete: role.relation.onDelete } : {}),
57
40
  };
58
41
  }
59
- return Object.keys(out).length ? out : undefined;
42
+ return Object.keys(descriptor).length ? descriptor : undefined;
60
43
  },
61
44
 
62
45
  reconstruct(wire, resolve?: Resolver) {
63
- const out: RoleRules = {};
64
- if (wire.primary) out.primary = true;
65
- if (wire.unique?.length) out.rules = wire.unique.map((group) => new Unique(group));
66
- if (wire.index) out.index = true;
46
+ const rules: RoleRules = {};
47
+ if (wire.primary) rules.primary = true;
48
+ if (wire.unique?.some((group) => group.length === 1)) rules.unique = true;
49
+ if (wire.index) rules.index = true;
67
50
  if (wire.relation) {
68
- // `judge` cannot serve here: it demands a thunk where a card carries a name.
51
+ // `validator` cannot serve here: it demands `() => Post` where a card carries a name.
69
52
  if (!oneOfTokens(wire.relation.kind, RELATION_KINDS)) {
70
53
  refuse(
71
54
  `role.relation.kind is ${JSON.stringify(wire.relation.kind)}`,
@@ -79,17 +62,22 @@ export const roleAxis: Axis<RoleRules, RoleDescriptor> = {
79
62
  );
80
63
  }
81
64
  const name = wire.relation.to;
82
- out.relation = {
65
+ rules.relation = {
83
66
  to: () => (resolve?.(name) ?? ({ name } as unknown)) as EntityConstructor,
84
67
  kind: wire.relation.kind as Relation['kind'],
85
68
  ...(wire.relation.onDelete ? { onDelete: wire.relation.onDelete as Relation['onDelete'] } : {}),
86
69
  } as RoleRules['relation'];
87
70
  }
88
- return out;
71
+ return rules;
89
72
  },
90
73
  };
91
74
 
92
- function judgeRelation(relation: unknown, errors: ValidationError[]): void {
75
+ /**
76
+ * So a relation is validated where `() => Post` is required — a card carries a name.
77
+ * FR : pour qu'une relation soit jugée là où `() => Post` est exigé, contrairement à une carte.
78
+ * `{ to: User, kind: 'one' }` → error `Expected a function returning the target entity, such as () => Post`
79
+ */
80
+ function validateRelation(relation: unknown, errors: ValidationError[]): void {
93
81
  if (!isObject(relation)) {
94
82
  errors.push({ path: 'role.relation', message: `Expected an object — got ${JSON.stringify(relation)}` });
95
83
  return;
@@ -101,7 +89,7 @@ function judgeRelation(relation: unknown, errors: ValidationError[]): void {
101
89
  });
102
90
  }
103
91
  if (typeof relation.to !== 'function') {
104
- errors.push({ path: 'role.relation.to', message: 'Expected a thunk returning the target entity' });
92
+ errors.push({ path: 'role.relation.to', message: 'Expected a function returning the target entity, such as () => Post' });
105
93
  }
106
94
  if (relation.onDelete !== undefined && !oneOfTokens(relation.onDelete, ON_DELETE)) {
107
95
  errors.push({
@@ -0,0 +1,20 @@
1
+ import { Registry } from '../../lib/Registry.js';
2
+
3
+ export type StringFormat =
4
+ | 'date-time'
5
+ | 'date'
6
+ | 'time'
7
+ | 'email'
8
+ | 'uuid'
9
+ | 'uri'
10
+ | (string & {});
11
+
12
+ export type FormatPredicate = (value: string) => boolean;
13
+
14
+ /**
15
+ * String formats the JSON Schema engine does not know. `find` answers `undefined` for
16
+ * `email` or `uuid`, which it validates on its own.
17
+ * FR : les formats de chaîne que le moteur ignore ; `find` rend `undefined` pour les siens.
18
+ * `Formats.register('siret', (v) => /^\d{14}$/.test(v))`
19
+ */
20
+ export const Formats = new Registry<FormatPredicate>('format');
@@ -6,7 +6,7 @@ type Nullably<T extends string> = T | readonly [T, 'null'];
6
6
  interface StringConstraints { minLength?: number; maxLength?: number; pattern?: string; enum?: readonly (string | null)[]; format?: StringFormat }
7
7
  interface NumericConstraints { minimum?: number; maximum?: number }
8
8
  interface ArrayConstraints { items?: Shape; minItems?: number; maxItems?: number }
9
- interface ObjectConstraints { properties?: Record<string, unknown>; required?: readonly string[] }
9
+ interface ObjectConstraints { properties?: Record<string, unknown>; required?: readonly string[]; additionalProperties?: boolean | Shape; propertyNames?: Shape }
10
10
 
11
11
  export type Shape =
12
12
  | ({ type: Nullably<'string'> } & StringConstraints)
@@ -19,8 +19,8 @@ const SHAPE_TYPES = ['string', 'number', 'integer', 'boolean', 'array', 'object'
19
19
 
20
20
  function isShapeImpl(value: unknown): value is Shape {
21
21
  if (typeof value !== 'object' || value === null) return false;
22
- const declared = (value as Shape).type;
23
- const names = Array.isArray(declared) ? declared : [declared];
22
+ const type = (value as Shape).type;
23
+ const names = Array.isArray(type) ? type : [type];
24
24
  return (
25
25
  names.some((name) => (SHAPE_TYPES as readonly unknown[]).includes(name)) &&
26
26
  names.every((name) => name === 'null' || (SHAPE_TYPES as readonly unknown[]).includes(name))
@@ -36,19 +36,19 @@ type BaseShape =
36
36
 
37
37
  function nullableShapeImpl(shape: Shape): Shape {
38
38
  if (Array.isArray(shape.type)) return shape;
39
- const out = { ...shape, type: [shape.type, 'null'] } as unknown as Shape;
40
- if ('enum' in out && out.enum && !out.enum.includes(null)) {
41
- (out as { enum: readonly (string | null)[] }).enum = [...out.enum, null];
39
+ const nullable = { ...shape, type: [shape.type, 'null'] } as unknown as Shape;
40
+ if ('enum' in nullable && nullable.enum && !nullable.enum.includes(null)) {
41
+ (nullable as { enum: readonly (string | null)[] }).enum = [...nullable.enum, null];
42
42
  }
43
- return out;
43
+ return nullable;
44
44
  }
45
45
 
46
- interface ShapeAnatomy {
46
+ interface ShapeParts {
47
47
  base?: BaseShape;
48
48
  nullable: boolean;
49
49
  }
50
50
 
51
- export class Anatomy {
51
+ export class Shapes {
52
52
  static is(value: unknown): value is Shape {
53
53
  return isShapeImpl(value);
54
54
  }
@@ -57,26 +57,26 @@ export class Anatomy {
57
57
  return nullableShapeImpl(shape);
58
58
  }
59
59
 
60
- private static readonly cache = new WeakMap<object, ShapeAnatomy>();
61
- private static readonly none: ShapeAnatomy = { base: undefined, nullable: false };
60
+ private static readonly cache = new WeakMap<object, ShapeParts>();
61
+ private static readonly none: ShapeParts = { base: undefined, nullable: false };
62
62
 
63
- static of(shape?: Shape): ShapeAnatomy {
63
+ static of(shape?: Shape): ShapeParts {
64
64
  if (!shape) return this.none;
65
- let a = this.cache.get(shape);
66
- if (!a) {
65
+ let parts = this.cache.get(shape);
66
+ if (!parts) {
67
67
  if (Array.isArray(shape.type)) {
68
68
  const baseType = shape.type.find((t) => t !== 'null');
69
69
  const base = { ...shape, type: baseType } as BaseShape;
70
70
  if ('enum' in base && base.enum) {
71
71
  (base as { enum: readonly (string | null)[] }).enum = base.enum.filter((v) => v !== null);
72
72
  }
73
- a = { base, nullable: true };
73
+ parts = { base, nullable: true };
74
74
  } else {
75
- a = { base: shape as BaseShape, nullable: false };
75
+ parts = { base: shape as BaseShape, nullable: false };
76
76
  }
77
- this.cache.set(shape, a);
77
+ this.cache.set(shape, parts);
78
78
  }
79
- return a;
79
+ return parts;
80
80
  }
81
81
 
82
82
  static isNullable(shape?: Shape): boolean {
@@ -0,0 +1,81 @@
1
+ import type { Fields } from '../field/Field.js';
2
+ import { isObject } from '../lib/utils.js';
3
+ import type { EntityAdapters } from './EntityAdapters.js';
4
+
5
+ type AdapterConfiguration = Record<string, unknown>;
6
+ type AdapterConfigurations = Record<string, AdapterConfiguration>;
7
+
8
+ export class EntityAdapterSet {
9
+ /**
10
+ * `{ sql: { body: { columnType: { pg: 'tsvector' } } } }`
11
+ */
12
+ private constructor(readonly adapters: EntityAdapters<Fields>) {}
13
+
14
+ /**
15
+ * Stops a malformed declaration from propagating, by checking its two levels.
16
+ * `of({ sql: { body: 3 } })` → passes; `of({ sql: 'oops' })` → throws, naming `adapters.sql`
17
+ */
18
+ static of(adapters?: EntityAdapters<Fields>): EntityAdapterSet {
19
+ if (!adapters) return new EntityAdapterSet({});
20
+
21
+ if (!isObject(adapters)) {
22
+ throw new Error(
23
+ `adapters: expected an object keyed by adapter name, got ${typeof adapters}.`,
24
+ );
25
+ }
26
+
27
+ for (const [adapter, fields] of Object.entries(adapters)) {
28
+ if (!isObject(fields)) {
29
+ throw new Error(
30
+ `adapters.${adapter}: expected an object keyed by field name, got ${typeof fields}. ` +
31
+ `What an adapter is handed is addressed by the field it applies to.`,
32
+ );
33
+ }
34
+ }
35
+
36
+ return new EntityAdapterSet(adapters);
37
+ }
38
+
39
+ /** Folds several into one, a later field entry replacing the earlier. Takes sets only, so */
40
+ static merged(adapterSets: readonly EntityAdapterSet[]): EntityAdapterSet {
41
+ const configurations: AdapterConfigurations = {};
42
+
43
+ for (const adapterSet of adapterSets)
44
+ for (const [adapter, configuration] of adapterSet.entries)
45
+ configurations[adapter] = { ...configurations[adapter], ...configuration };
46
+
47
+ return new EntityAdapterSet(configurations);
48
+ }
49
+
50
+ private get entries(): [string, AdapterConfiguration][] {
51
+ return Object.entries(this.adapters);
52
+ }
53
+
54
+ /** Collects every field addressed, so a declaration aimed at a missing field is caught. */
55
+ get fieldNames(): string[] {
56
+ return [
57
+ ...new Set(this.entries.flatMap(([, configuration]) => Object.keys(configuration))),
58
+ ];
59
+ }
60
+
61
+ /** Follows a derivation — `pick`, `omit` and `rename` are one gesture: what remains, and */
62
+ rename(transform: (key: string) => string | undefined): EntityAdapterSet {
63
+ const renamed: AdapterConfigurations = {};
64
+
65
+ for (const [adapter, configuration] of this.entries) {
66
+ const mapped: AdapterConfiguration = {};
67
+
68
+ for (const [key, entry] of Object.entries(configuration)) {
69
+ const newName = transform(key);
70
+
71
+ if (newName !== undefined) mapped[newName] = entry;
72
+ }
73
+
74
+ // Drop an adapter left with no field. `Post.pick('id')` would otherwise answer
75
+ // `{ sql: {} }` — a line the entity never wrote.
76
+ if (Object.keys(mapped).length) renamed[adapter] = mapped;
77
+ }
78
+
79
+ return new EntityAdapterSet(renamed);
80
+ }
81
+ }