@fougere/schema 0.5.0-alpha.1 → 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 (403) hide show
  1. package/README.md +5 -4
  2. package/dist/{schema/Schema.d.ts → Schema.d.ts} +18 -28
  3. package/dist/Schema.d.ts.map +1 -0
  4. package/dist/Schema.js +136 -0
  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/axis/Axis.d.ts +2 -2
  18. package/dist/axis/Axis.d.ts.map +1 -1
  19. package/dist/axis/boundary/Boundaries.d.ts +13 -12
  20. package/dist/axis/boundary/Boundaries.d.ts.map +1 -1
  21. package/dist/axis/boundary/Boundaries.js +18 -24
  22. package/dist/axis/boundary/Boundaries.js.map +1 -1
  23. package/dist/axis/boundary/Boundary.d.ts +3 -1
  24. package/dist/axis/boundary/Boundary.d.ts.map +1 -1
  25. package/dist/axis/boundary/Boundary.js +16 -7
  26. package/dist/axis/boundary/Boundary.js.map +1 -1
  27. package/dist/axis/boundary/BoundaryAxis.js +4 -4
  28. package/dist/axis/boundary/BoundaryAxis.js.map +1 -1
  29. package/dist/axis/lifecycle/Clock.d.ts +1 -1
  30. package/dist/axis/lifecycle/Clock.d.ts.map +1 -1
  31. package/dist/axis/lifecycle/Clock.js +1 -1
  32. package/dist/axis/lifecycle/Clock.js.map +1 -1
  33. package/dist/axis/lifecycle/Generators.d.ts +6 -14
  34. package/dist/axis/lifecycle/Generators.d.ts.map +1 -1
  35. package/dist/axis/lifecycle/Generators.js +10 -28
  36. package/dist/axis/lifecycle/Generators.js.map +1 -1
  37. package/dist/axis/lifecycle/LifecycleAxis.js +11 -6
  38. package/dist/axis/lifecycle/LifecycleAxis.js.map +1 -1
  39. package/dist/axis/lifecycle/apply.d.ts +14 -5
  40. package/dist/axis/lifecycle/apply.d.ts.map +1 -1
  41. package/dist/axis/lifecycle/apply.js +26 -10
  42. package/dist/axis/lifecycle/apply.js.map +1 -1
  43. package/dist/axis/role/Relation.d.ts +3 -2
  44. package/dist/axis/role/Relation.d.ts.map +1 -1
  45. package/dist/axis/role/Relation.js +6 -5
  46. package/dist/axis/role/Relation.js.map +1 -1
  47. package/dist/axis/role/Role.d.ts +6 -6
  48. package/dist/axis/role/Role.d.ts.map +1 -1
  49. package/dist/axis/role/Role.js +13 -17
  50. package/dist/axis/role/Role.js.map +1 -1
  51. package/dist/axis/role/RoleAxis.d.ts +1 -1
  52. package/dist/axis/role/RoleAxis.d.ts.map +1 -1
  53. package/dist/axis/role/RoleAxis.js +28 -37
  54. package/dist/axis/role/RoleAxis.js.map +1 -1
  55. package/dist/axis/shape/Formats.d.ts +8 -5
  56. package/dist/axis/shape/Formats.d.ts.map +1 -1
  57. package/dist/axis/shape/Formats.js +8 -9
  58. package/dist/axis/shape/Formats.js.map +1 -1
  59. package/dist/axis/shape/Shape.d.ts +5 -3
  60. package/dist/axis/shape/Shape.d.ts.map +1 -1
  61. package/dist/axis/shape/Shape.js +13 -13
  62. package/dist/axis/shape/Shape.js.map +1 -1
  63. package/dist/entity/EntityAdapterSet.d.ts +22 -0
  64. package/dist/entity/EntityAdapterSet.d.ts.map +1 -0
  65. package/dist/entity/EntityAdapterSet.js +63 -0
  66. package/dist/entity/EntityAdapterSet.js.map +1 -0
  67. package/dist/entity/EntityAdapters.d.ts +5 -0
  68. package/dist/entity/EntityAdapters.d.ts.map +1 -0
  69. package/dist/entity/EntityAdapters.js +2 -0
  70. package/dist/entity/EntityAdapters.js.map +1 -0
  71. package/dist/{EntityDeclarations.d.ts → entity/EntityDeclarations.d.ts} +2 -7
  72. package/dist/entity/EntityDeclarations.d.ts.map +1 -0
  73. package/dist/entity/EntityDeclarations.js.map +1 -0
  74. package/dist/entity.d.ts +8 -3
  75. package/dist/entity.d.ts.map +1 -1
  76. package/dist/entity.js +6 -1
  77. package/dist/entity.js.map +1 -1
  78. package/dist/{fields → field}/Field.d.ts +8 -9
  79. package/dist/field/Field.d.ts.map +1 -0
  80. package/dist/field/Field.js +35 -0
  81. package/dist/field/Field.js.map +1 -0
  82. package/dist/field/FieldSet.d.ts +22 -0
  83. package/dist/field/FieldSet.d.ts.map +1 -0
  84. package/dist/field/FieldSet.js +58 -0
  85. package/dist/field/FieldSet.js.map +1 -0
  86. package/dist/index.d.ts +24 -28
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.js +17 -21
  89. package/dist/index.js.map +1 -1
  90. package/dist/lib/Registry.d.ts +12 -0
  91. package/dist/lib/Registry.d.ts.map +1 -0
  92. package/dist/lib/Registry.js +29 -0
  93. package/dist/lib/Registry.js.map +1 -0
  94. package/dist/{utils.d.ts → lib/utils.d.ts} +1 -0
  95. package/dist/lib/utils.d.ts.map +1 -0
  96. package/dist/{utils.js → lib/utils.js} +1 -0
  97. package/dist/lib/utils.js.map +1 -0
  98. package/dist/projection/Cases.d.ts +39 -0
  99. package/dist/projection/Cases.d.ts.map +1 -0
  100. package/dist/projection/Cases.js +146 -0
  101. package/dist/projection/Cases.js.map +1 -0
  102. package/dist/projection/Visibility.d.ts +2 -1
  103. package/dist/projection/Visibility.d.ts.map +1 -1
  104. package/dist/projection/Visibility.js +1 -0
  105. package/dist/projection/Visibility.js.map +1 -1
  106. package/dist/{card → projection/card}/Bundle.d.ts +1 -1
  107. package/dist/projection/card/Bundle.d.ts.map +1 -0
  108. package/dist/{card → projection/card}/Bundle.js +7 -7
  109. package/dist/projection/card/Bundle.js.map +1 -0
  110. package/dist/projection/card/Card.d.ts +21 -0
  111. package/dist/projection/card/Card.d.ts.map +1 -0
  112. package/dist/{card → projection/card}/Card.js +119 -58
  113. package/dist/projection/card/Card.js.map +1 -0
  114. package/dist/{card → projection/card}/Descriptor.d.ts +7 -7
  115. package/dist/projection/card/Descriptor.d.ts.map +1 -0
  116. package/dist/projection/card/Descriptor.js.map +1 -0
  117. package/dist/projection/card/admission.d.ts +20 -0
  118. package/dist/projection/card/admission.d.ts.map +1 -0
  119. package/dist/projection/card/admission.js +27 -0
  120. package/dist/projection/card/admission.js.map +1 -0
  121. package/dist/{card → projection/card}/diff.d.ts +3 -3
  122. package/dist/projection/card/diff.d.ts.map +1 -0
  123. package/dist/{card → projection/card}/diff.js.map +1 -1
  124. package/dist/{judge/result.d.ts → validation.d.ts} +1 -1
  125. package/dist/validation.d.ts.map +1 -0
  126. package/dist/validation.js +2 -0
  127. package/dist/validation.js.map +1 -0
  128. package/dist/validator/AdapterFieldValidator.d.ts +14 -0
  129. package/dist/validator/AdapterFieldValidator.d.ts.map +1 -0
  130. package/dist/validator/AdapterFieldValidator.js +36 -0
  131. package/dist/validator/AdapterFieldValidator.js.map +1 -0
  132. package/dist/validator/FieldDeclarationValidator.d.ts +9 -0
  133. package/dist/validator/FieldDeclarationValidator.d.ts.map +1 -0
  134. package/dist/{judge/FieldJudge.js → validator/FieldDeclarationValidator.js} +13 -13
  135. package/dist/validator/FieldDeclarationValidator.js.map +1 -0
  136. package/dist/validator/FieldValueValidator.d.ts +13 -0
  137. package/dist/validator/FieldValueValidator.d.ts.map +1 -0
  138. package/dist/{judge/ValueJudge.js → validator/FieldValueValidator.js} +14 -11
  139. package/dist/validator/FieldValueValidator.js.map +1 -0
  140. package/dist/{judge/RowRefusal.d.ts → validator/InputRefusal.d.ts} +3 -4
  141. package/dist/validator/InputRefusal.d.ts.map +1 -0
  142. package/dist/{judge/RowRefusal.js → validator/InputRefusal.js} +3 -3
  143. package/dist/validator/InputRefusal.js.map +1 -0
  144. package/dist/validator/InputValidator.d.ts +15 -0
  145. package/dist/validator/InputValidator.d.ts.map +1 -0
  146. package/dist/{judge/RowJudge.js → validator/InputValidator.js} +21 -17
  147. package/dist/validator/InputValidator.js.map +1 -0
  148. package/dist/vocabulary/bool.d.ts +6 -1
  149. package/dist/vocabulary/bool.d.ts.map +1 -1
  150. package/dist/vocabulary/bool.js +6 -1
  151. package/dist/vocabulary/bool.js.map +1 -1
  152. package/dist/vocabulary/created.d.ts +6 -1
  153. package/dist/vocabulary/created.d.ts.map +1 -1
  154. package/dist/vocabulary/created.js +6 -1
  155. package/dist/vocabulary/created.js.map +1 -1
  156. package/dist/vocabulary/date.d.ts +6 -1
  157. package/dist/vocabulary/date.d.ts.map +1 -1
  158. package/dist/vocabulary/date.js +6 -1
  159. package/dist/vocabulary/date.js.map +1 -1
  160. package/dist/vocabulary/email.d.ts +6 -1
  161. package/dist/vocabulary/email.d.ts.map +1 -1
  162. package/dist/vocabulary/email.js +5 -0
  163. package/dist/vocabulary/email.js.map +1 -1
  164. package/dist/vocabulary/immutable.d.ts +6 -1
  165. package/dist/vocabulary/immutable.d.ts.map +1 -1
  166. package/dist/vocabulary/immutable.js +5 -0
  167. package/dist/vocabulary/immutable.js.map +1 -1
  168. package/dist/vocabulary/indexed.d.ts +6 -1
  169. package/dist/vocabulary/indexed.d.ts.map +1 -1
  170. package/dist/vocabulary/indexed.js +8 -1
  171. package/dist/vocabulary/indexed.js.map +1 -1
  172. package/dist/vocabulary/json.d.ts +2 -2
  173. package/dist/vocabulary/json.d.ts.map +1 -1
  174. package/dist/vocabulary/json.js +9 -4
  175. package/dist/vocabulary/json.js.map +1 -1
  176. package/dist/vocabulary/list.d.ts +6 -1
  177. package/dist/vocabulary/list.d.ts.map +1 -1
  178. package/dist/vocabulary/list.js +6 -1
  179. package/dist/vocabulary/list.js.map +1 -1
  180. package/dist/vocabulary/many.d.ts +6 -1
  181. package/dist/vocabulary/many.d.ts.map +1 -1
  182. package/dist/vocabulary/many.js +6 -1
  183. package/dist/vocabulary/many.js.map +1 -1
  184. package/dist/vocabulary/nullable.d.ts +6 -1
  185. package/dist/vocabulary/nullable.d.ts.map +1 -1
  186. package/dist/vocabulary/nullable.js +7 -2
  187. package/dist/vocabulary/nullable.js.map +1 -1
  188. package/dist/vocabulary/number.d.ts +6 -1
  189. package/dist/vocabulary/number.d.ts.map +1 -1
  190. package/dist/vocabulary/number.js +11 -2
  191. package/dist/vocabulary/number.js.map +1 -1
  192. package/dist/vocabulary/oneOf.d.ts +1 -1
  193. package/dist/vocabulary/oneOf.d.ts.map +1 -1
  194. package/dist/vocabulary/oneOf.js +6 -1
  195. package/dist/vocabulary/oneOf.js.map +1 -1
  196. package/dist/vocabulary/optional.d.ts +6 -1
  197. package/dist/vocabulary/optional.d.ts.map +1 -1
  198. package/dist/vocabulary/optional.js +10 -3
  199. package/dist/vocabulary/optional.js.map +1 -1
  200. package/dist/vocabulary/primary.d.ts +1 -1
  201. package/dist/vocabulary/primary.d.ts.map +1 -1
  202. package/dist/vocabulary/primary.js +10 -5
  203. package/dist/vocabulary/primary.js.map +1 -1
  204. package/dist/vocabulary/readOnly.d.ts +6 -1
  205. package/dist/vocabulary/readOnly.d.ts.map +1 -1
  206. package/dist/vocabulary/readOnly.js +5 -0
  207. package/dist/vocabulary/readOnly.js.map +1 -1
  208. package/dist/vocabulary/ref.d.ts +6 -1
  209. package/dist/vocabulary/ref.d.ts.map +1 -1
  210. package/dist/vocabulary/ref.js +6 -1
  211. package/dist/vocabulary/ref.js.map +1 -1
  212. package/dist/vocabulary/text.d.ts +6 -1
  213. package/dist/vocabulary/text.d.ts.map +1 -1
  214. package/dist/vocabulary/text.js +13 -2
  215. package/dist/vocabulary/text.js.map +1 -1
  216. package/dist/vocabulary/unique.d.ts +6 -1
  217. package/dist/vocabulary/unique.d.ts.map +1 -1
  218. package/dist/vocabulary/unique.js +8 -4
  219. package/dist/vocabulary/unique.js.map +1 -1
  220. package/dist/vocabulary/updated.d.ts +6 -1
  221. package/dist/vocabulary/updated.d.ts.map +1 -1
  222. package/dist/vocabulary/updated.js +5 -0
  223. package/dist/vocabulary/updated.js.map +1 -1
  224. package/dist/vocabulary/url.d.ts +6 -1
  225. package/dist/vocabulary/url.d.ts.map +1 -1
  226. package/dist/vocabulary/url.js +5 -0
  227. package/dist/vocabulary/url.js.map +1 -1
  228. package/dist/vocabulary/vocabulary.d.ts +6 -1
  229. package/dist/vocabulary/vocabulary.d.ts.map +1 -1
  230. package/dist/vocabulary/vocabulary.js +21 -11
  231. package/dist/vocabulary/vocabulary.js.map +1 -1
  232. package/dist/vocabulary/writeOnly.d.ts +6 -1
  233. package/dist/vocabulary/writeOnly.d.ts.map +1 -1
  234. package/dist/vocabulary/writeOnly.js +5 -0
  235. package/dist/vocabulary/writeOnly.js.map +1 -1
  236. package/package.json +1 -1
  237. package/src/Schema.ts +208 -0
  238. package/src/SchemaDefinition.ts +257 -0
  239. package/src/SchemaDerivation.ts +44 -0
  240. package/src/{schema/SchemaView.ts → SchemaView.ts} +9 -9
  241. package/src/axis/Axis.ts +2 -2
  242. package/src/axis/boundary/Boundaries.ts +25 -32
  243. package/src/axis/boundary/Boundary.ts +23 -10
  244. package/src/axis/boundary/BoundaryAxis.ts +4 -4
  245. package/src/axis/lifecycle/Clock.ts +1 -1
  246. package/src/axis/lifecycle/Generators.ts +12 -29
  247. package/src/axis/lifecycle/LifecycleAxis.ts +12 -7
  248. package/src/axis/lifecycle/apply.ts +30 -15
  249. package/src/axis/role/Relation.ts +6 -5
  250. package/src/axis/role/Role.ts +16 -20
  251. package/src/axis/role/RoleAxis.ts +28 -41
  252. package/src/axis/shape/Formats.ts +9 -11
  253. package/src/axis/shape/Shape.ts +18 -18
  254. package/src/entity/EntityAdapterSet.ts +81 -0
  255. package/src/entity/EntityAdapters.ts +8 -0
  256. package/src/entity/EntityDeclarations.ts +16 -0
  257. package/src/entity.ts +8 -3
  258. package/src/field/Field.ts +61 -0
  259. package/src/field/FieldSet.ts +75 -0
  260. package/src/index.ts +50 -28
  261. package/src/lib/Registry.ts +37 -0
  262. package/src/{utils.ts → lib/utils.ts} +3 -0
  263. package/src/projection/Cases.ts +176 -0
  264. package/src/projection/Visibility.ts +6 -2
  265. package/src/{card → projection/card}/Bundle.ts +9 -9
  266. package/src/projection/card/Card.ts +342 -0
  267. package/src/{card → projection/card}/Descriptor.ts +7 -7
  268. package/src/projection/card/admission.ts +40 -0
  269. package/src/{card → projection/card}/diff.ts +3 -3
  270. package/src/validator/AdapterFieldValidator.ts +40 -0
  271. package/src/{judge/FieldJudge.ts → validator/FieldDeclarationValidator.ts} +15 -15
  272. package/src/{judge/ValueJudge.ts → validator/FieldValueValidator.ts} +18 -13
  273. package/src/{judge/RowRefusal.ts → validator/InputRefusal.ts} +2 -4
  274. package/src/{judge/RowJudge.ts → validator/InputValidator.ts} +27 -20
  275. package/src/vocabulary/bool.ts +8 -2
  276. package/src/vocabulary/created.ts +7 -2
  277. package/src/vocabulary/date.ts +6 -1
  278. package/src/vocabulary/email.ts +6 -1
  279. package/src/vocabulary/immutable.ts +12 -4
  280. package/src/vocabulary/indexed.ts +9 -2
  281. package/src/vocabulary/json.ts +13 -6
  282. package/src/vocabulary/list.ts +10 -2
  283. package/src/vocabulary/many.ts +9 -2
  284. package/src/vocabulary/nullable.ts +13 -5
  285. package/src/vocabulary/number.ts +13 -3
  286. package/src/vocabulary/oneOf.ts +11 -3
  287. package/src/vocabulary/optional.ts +16 -6
  288. package/src/vocabulary/primary.ts +10 -5
  289. package/src/vocabulary/readOnly.ts +12 -4
  290. package/src/vocabulary/ref.ts +10 -2
  291. package/src/vocabulary/text.ts +15 -3
  292. package/src/vocabulary/unique.ts +10 -9
  293. package/src/vocabulary/updated.ts +6 -1
  294. package/src/vocabulary/url.ts +6 -1
  295. package/src/vocabulary/vocabulary.ts +21 -12
  296. package/src/vocabulary/writeOnly.ts +12 -4
  297. package/dist/EntityAdapters.d.ts +0 -28
  298. package/dist/EntityAdapters.d.ts.map +0 -1
  299. package/dist/EntityAdapters.js +0 -75
  300. package/dist/EntityAdapters.js.map +0 -1
  301. package/dist/EntityDeclarations.d.ts.map +0 -1
  302. package/dist/EntityDeclarations.js.map +0 -1
  303. package/dist/card/Bundle.d.ts.map +0 -1
  304. package/dist/card/Bundle.js.map +0 -1
  305. package/dist/card/Card.d.ts +0 -27
  306. package/dist/card/Card.d.ts.map +0 -1
  307. package/dist/card/Card.js.map +0 -1
  308. package/dist/card/Descriptor.d.ts.map +0 -1
  309. package/dist/card/Descriptor.js.map +0 -1
  310. package/dist/card/EntityTypeSource.d.ts +0 -12
  311. package/dist/card/EntityTypeSource.d.ts.map +0 -1
  312. package/dist/card/EntityTypeSource.js +0 -78
  313. package/dist/card/EntityTypeSource.js.map +0 -1
  314. package/dist/card/FacadeTypeSource.d.ts +0 -19
  315. package/dist/card/FacadeTypeSource.d.ts.map +0 -1
  316. package/dist/card/FacadeTypeSource.js +0 -40
  317. package/dist/card/FacadeTypeSource.js.map +0 -1
  318. package/dist/card/admission.d.ts +0 -9
  319. package/dist/card/admission.d.ts.map +0 -1
  320. package/dist/card/admission.js +0 -16
  321. package/dist/card/admission.js.map +0 -1
  322. package/dist/card/diff.d.ts.map +0 -1
  323. package/dist/constraint/FieldGroup.d.ts +0 -21
  324. package/dist/constraint/FieldGroup.d.ts.map +0 -1
  325. package/dist/constraint/FieldGroup.js +0 -65
  326. package/dist/constraint/FieldGroup.js.map +0 -1
  327. package/dist/constraint/Unique.d.ts +0 -4
  328. package/dist/constraint/Unique.d.ts.map +0 -1
  329. package/dist/constraint/Unique.js +0 -4
  330. package/dist/constraint/Unique.js.map +0 -1
  331. package/dist/fields/Field.d.ts.map +0 -1
  332. package/dist/fields/Field.js +0 -52
  333. package/dist/fields/Field.js.map +0 -1
  334. package/dist/fields/FieldSet.d.ts +0 -28
  335. package/dist/fields/FieldSet.d.ts.map +0 -1
  336. package/dist/fields/FieldSet.js +0 -55
  337. package/dist/fields/FieldSet.js.map +0 -1
  338. package/dist/judge/Cases.d.ts +0 -50
  339. package/dist/judge/Cases.d.ts.map +0 -1
  340. package/dist/judge/Cases.js +0 -108
  341. package/dist/judge/Cases.js.map +0 -1
  342. package/dist/judge/FieldJudge.d.ts +0 -9
  343. package/dist/judge/FieldJudge.d.ts.map +0 -1
  344. package/dist/judge/FieldJudge.js.map +0 -1
  345. package/dist/judge/RowJudge.d.ts +0 -12
  346. package/dist/judge/RowJudge.d.ts.map +0 -1
  347. package/dist/judge/RowJudge.js.map +0 -1
  348. package/dist/judge/RowRefusal.d.ts.map +0 -1
  349. package/dist/judge/RowRefusal.js.map +0 -1
  350. package/dist/judge/ValueForm.d.ts +0 -3
  351. package/dist/judge/ValueForm.d.ts.map +0 -1
  352. package/dist/judge/ValueForm.js +0 -3
  353. package/dist/judge/ValueForm.js.map +0 -1
  354. package/dist/judge/ValueJudge.d.ts +0 -12
  355. package/dist/judge/ValueJudge.d.ts.map +0 -1
  356. package/dist/judge/ValueJudge.js.map +0 -1
  357. package/dist/judge/options.d.ts +0 -4
  358. package/dist/judge/options.d.ts.map +0 -1
  359. package/dist/judge/options.js +0 -2
  360. package/dist/judge/options.js.map +0 -1
  361. package/dist/judge/result.d.ts.map +0 -1
  362. package/dist/judge/result.js +0 -2
  363. package/dist/judge/result.js.map +0 -1
  364. package/dist/projection/SchemaOrCard.d.ts +0 -7
  365. package/dist/projection/SchemaOrCard.d.ts.map +0 -1
  366. package/dist/projection/SchemaOrCard.js +0 -11
  367. package/dist/projection/SchemaOrCard.js.map +0 -1
  368. package/dist/schema/Schema.d.ts.map +0 -1
  369. package/dist/schema/Schema.js +0 -150
  370. package/dist/schema/Schema.js.map +0 -1
  371. package/dist/schema/SchemaDefinition.d.ts +0 -60
  372. package/dist/schema/SchemaDefinition.d.ts.map +0 -1
  373. package/dist/schema/SchemaDefinition.js +0 -85
  374. package/dist/schema/SchemaDefinition.js.map +0 -1
  375. package/dist/schema/SchemaDerivation.d.ts +0 -34
  376. package/dist/schema/SchemaDerivation.d.ts.map +0 -1
  377. package/dist/schema/SchemaDerivation.js +0 -46
  378. package/dist/schema/SchemaDerivation.js.map +0 -1
  379. package/dist/schema/SchemaView.d.ts.map +0 -1
  380. package/dist/utils.d.ts.map +0 -1
  381. package/dist/utils.js.map +0 -1
  382. package/src/EntityAdapters.ts +0 -100
  383. package/src/EntityDeclarations.ts +0 -19
  384. package/src/card/Card.ts +0 -256
  385. package/src/card/EntityTypeSource.ts +0 -90
  386. package/src/card/FacadeTypeSource.ts +0 -55
  387. package/src/card/admission.ts +0 -25
  388. package/src/constraint/FieldGroup.ts +0 -91
  389. package/src/constraint/Unique.ts +0 -3
  390. package/src/fields/Field.ts +0 -80
  391. package/src/fields/FieldSet.ts +0 -62
  392. package/src/judge/Cases.ts +0 -139
  393. package/src/judge/ValueForm.ts +0 -7
  394. package/src/judge/options.ts +0 -3
  395. package/src/projection/SchemaOrCard.ts +0 -18
  396. package/src/schema/Schema.ts +0 -197
  397. package/src/schema/SchemaDefinition.ts +0 -120
  398. package/src/schema/SchemaDerivation.ts +0 -57
  399. /package/dist/{schema/SchemaView.js → SchemaView.js} +0 -0
  400. /package/dist/{EntityDeclarations.js → entity/EntityDeclarations.js} +0 -0
  401. /package/dist/{card → projection/card}/Descriptor.js +0 -0
  402. /package/dist/{card → projection/card}/diff.js +0 -0
  403. /package/src/{judge/result.ts → validation.ts} +0 -0
@@ -1,7 +1,7 @@
1
1
  import { Boundary } from '../axis/boundary/Boundary.js';
2
2
  import { Lifecycle } from '../axis/lifecycle/Lifecycle.js';
3
3
  import { Role } from '../axis/role/Role.js';
4
- import type { Fields } from '../fields/Field.js';
4
+ import type { Fields } from '../field/Field.js';
5
5
 
6
6
  export class Visibility {
7
7
  private constructor(private readonly fields: Fields) {}
@@ -10,6 +10,7 @@ export class Visibility {
10
10
  return new Visibility(fields);
11
11
  }
12
12
 
13
+ /** A write-only secret is absent from the type, not just from the value. */
13
14
  get input(): Fields {
14
15
  const result: Fields = {};
15
16
  for (const [name, field] of Object.entries(this.fields)) {
@@ -37,7 +38,10 @@ export class Visibility {
37
38
  for (const [key, field] of Object.entries(this.fields)) {
38
39
  if (!(key in record)) continue;
39
40
  const boundary = Boundary.of(field);
40
- if (boundary.writeOnly) { delete out[key]; continue; }
41
+ if (boundary.writeOnly) {
42
+ delete out[key];
43
+ continue;
44
+ }
41
45
  const value = record[key];
42
46
  if (value === null || value === undefined) continue;
43
47
  out[key] = boundary.encode(value);
@@ -1,7 +1,7 @@
1
- import type { Resolver } from '../axis/Axis.js';
2
- import type { EntityConstructor } from '../axis/role/Relation.js';
3
- import { lowerFirst } from '../utils.js';
4
- import type { SchemaView } from '../schema/SchemaView.js';
1
+ import type { Resolver } from '../../axis/Axis.js';
2
+ import type { EntityConstructor } from '../../axis/role/Relation.js';
3
+ import { lowerFirst } from '../../lib/utils.js';
4
+ import type { SchemaView } from '../../SchemaView.js';
5
5
  import { Card } from './Card.js';
6
6
  import type { SchemaBundle, SchemaDescriptor } from './Descriptor.js';
7
7
  import type { Diff, SetDiff, SetDiffOptions } from './diff.js';
@@ -47,12 +47,12 @@ export class Bundle {
47
47
  }
48
48
 
49
49
  toSchemas(): Record<string, SchemaView> {
50
- const map: Record<string, EntityConstructor> = {};
51
- const resolve: Resolver = (name) => map[name.toLowerCase()];
50
+ const byName: Record<string, EntityConstructor> = {};
51
+ const resolve: Resolver = (name) => byName[name.toLowerCase()];
52
52
  const schemas: Record<string, SchemaView> = {};
53
53
  for (const [name, descriptor] of Object.entries(this.descriptor.$defs)) {
54
54
  const schema = Card.fromDescriptor(descriptor).toSchema(resolve, name);
55
- map[name.toLowerCase()] = schema as unknown as EntityConstructor;
55
+ byName[name.toLowerCase()] = schema as unknown as EntityConstructor;
56
56
  schemas[name] = schema;
57
57
  }
58
58
  return schemas;
@@ -65,11 +65,11 @@ export class Bundle {
65
65
  for (const [name, descriptor] of Object.entries(before)) {
66
66
  const target = after[name];
67
67
  if (target === undefined) continue;
68
- const answer = Card.fromDescriptor(descriptor).diff(
68
+ const diff = Card.fromDescriptor(descriptor).diff(
69
69
  Card.fromDescriptor(target),
70
70
  { renamed: options.renamed?.[name] ?? {} },
71
71
  );
72
- if (answer.changes.length > 0 || answer.ambiguous.length > 0) entities[name] = answer;
72
+ if (diff.changes.length > 0 || diff.ambiguous.length > 0) entities[name] = diff;
73
73
  }
74
74
  return {
75
75
  entitiesAdded: Object.keys(after).filter((name) => !(name in before)),
@@ -0,0 +1,342 @@
1
+ import { EXTENSION_AXES, EXTENSION_SLOTS, type Resolver } from '../../axis/Axis.js';
2
+ import { dequal } from 'dequal';
3
+ import { clean, isObject } from '../../lib/utils.js';
4
+ import { Field, type Fields } from '../../field/Field.js';
5
+ import { deduplicated } from '../../field/FieldSet.js';
6
+ import { InputValidator } from '../../validator/InputValidator.js';
7
+ import { Schema, type SchemaConstructor } from '../../Schema.js';
8
+ import type { Values, SchemaView } from '../../SchemaView.js';
9
+ import { refuse } from './admission.js';
10
+ import type {
11
+ DerivedFrom,
12
+ FieldDescriptor,
13
+ FieldExtension,
14
+ SchemaDescriptor,
15
+ } from './Descriptor.js';
16
+ import type { Change, Diff, DiffOptions, RenameCandidate, TypeSet } from './diff.js';
17
+
18
+ type FieldsOf<T> = { [K in keyof T]-?: Field<T[K]> };
19
+
20
+ /** One portable schema description and every decision made about that description. */
21
+ export class Card<T = Values<Fields>> {
22
+ private constructor(readonly descriptor: SchemaDescriptor) {}
23
+
24
+ static fromSchema<TFields extends Fields>(
25
+ schema: SchemaView<TFields>,
26
+ name?: string,
27
+ ): Card<Values<TFields>> {
28
+ const fields = schema.getFields();
29
+ const validator = InputValidator.of(fields);
30
+ const properties: Record<string, FieldDescriptor> = {};
31
+ const required: string[] = [];
32
+ for (const [key, field] of Object.entries(fields)) {
33
+ properties[key] = describeField(field, key);
34
+ if (validator.onAbsent(field) === null) required.push(key);
35
+ }
36
+ for (const group of schema.getUnique() ?? [])
37
+ for (const member of group) carryGroup(properties[member], group);
38
+
39
+ const descriptor: SchemaDescriptor = {
40
+ type: 'object',
41
+ properties,
42
+ 'x-fougere-version': 1,
43
+ 'x-fougere-vendor': 'fougere',
44
+ };
45
+ const title = name ?? schema.derivation?.sourceName ?? schema.name;
46
+ if (title) descriptor.title = title;
47
+ if (required.length) descriptor.required = required;
48
+
49
+ const origin = originOf(schema);
50
+ if (origin) descriptor['x-fougere-derived'] = origin;
51
+ return new Card<Values<TFields>>(descriptor);
52
+ }
53
+
54
+ static fromDescriptor<T = Values<Fields>>(descriptor: SchemaDescriptor): Card<T> {
55
+ return new Card<T>(descriptor);
56
+ }
57
+
58
+ get origin(): DerivedFrom | undefined {
59
+ return this.descriptor['x-fougere-derived'];
60
+ }
61
+
62
+ toSchema(resolve?: Resolver, name?: string): SchemaConstructor<FieldsOf<T>> {
63
+ const descriptor = this.descriptor;
64
+ const subject = name ? `schema '${name}'` : 'this schema';
65
+ if (!isObject(descriptor))
66
+ refuse(`${subject} is not an object`, 'A card carries one JSON Schema per door.');
67
+ const version = descriptor['x-fougere-version'];
68
+ if (version !== 1) {
69
+ refuse(
70
+ `${subject} states \`x-fougere-version: ${JSON.stringify(version)}\` and this reader speaks 1`,
71
+ 'A producer and its readers move together: re-sync the consumer, or serve the version it speaks.',
72
+ );
73
+ }
74
+ if (!isObject(descriptor.properties)) {
75
+ refuse(
76
+ `${subject} carries no \`properties\` object`,
77
+ 'A schema is its fields; there is nothing to rebuild.',
78
+ );
79
+ }
80
+
81
+ const fields: Fields = {};
82
+ const groups: string[][] = [];
83
+ for (const [key, property] of Object.entries(descriptor.properties)) {
84
+ fields[key] = reconstructField(property, key, resolve);
85
+ for (const group of property['x-fougere']?.role?.unique ?? [])
86
+ if (group.length > 1) groups.push([...group]);
87
+ }
88
+ const schema = Schema.of({ fields, constraints: { unique: deduplicated(groups) } });
89
+ const title = name ?? descriptor.title;
90
+ if (title)
91
+ Object.defineProperty(schema, 'name', { value: title, configurable: true });
92
+ return schema as unknown as SchemaConstructor<FieldsOf<T>>;
93
+ }
94
+
95
+ diff(other: Card, options: DiffOptions = {}): Diff {
96
+ const changes: Change[] = [];
97
+ const renamed = options.renamed ?? {};
98
+ const before = this.descriptor.properties ?? {};
99
+ const after = other.descriptor.properties ?? {};
100
+ const requiredBefore = new Set(this.descriptor.required ?? []);
101
+ const requiredAfter = new Set(other.descriptor.required ?? []);
102
+
103
+ // Apply a declared rename first so subsequent differences use the new field name.
104
+ const nameAfter = (field: string): string => renamed[field] ?? field;
105
+ const removed: string[] = [];
106
+ for (const [field, descriptor] of Object.entries(before)) {
107
+ const now = nameAfter(field);
108
+ const target = after[now];
109
+ if (target === undefined) {
110
+ removed.push(field);
111
+ continue;
112
+ }
113
+
114
+ if (now !== field)
115
+ changes.push({ kind: 'renamed', from: field, to: now, field: target });
116
+
117
+ const wasType = typesOf(descriptor);
118
+ const isType = typesOf(target);
119
+ if (!dequal(wasType, isType))
120
+ changes.push({ kind: 'retyped', field: now, from: wasType, to: isType });
121
+ else if (!dequal(boundsOf(descriptor), boundsOf(target))) {
122
+ changes.push({ kind: 'reshaped', field: now, from: descriptor, to: target });
123
+ }
124
+
125
+ const wasRequired = requiredBefore.has(field);
126
+ const isRequired = requiredAfter.has(now);
127
+ if (wasRequired !== isRequired) {
128
+ changes.push({ kind: 'required', field: now, from: wasRequired, to: isRequired });
129
+ }
130
+ changes.push(...restated(now, descriptor['x-fougere'], target['x-fougere']));
131
+ }
132
+
133
+ const claimed = new Set(Object.values(renamed));
134
+ const added = Object.keys(after).filter(
135
+ (field) => !(field in before) && !claimed.has(field),
136
+ );
137
+ for (const field of removed) {
138
+ changes.push({
139
+ kind: 'removed',
140
+ field,
141
+ from: before[field],
142
+ required: requiredBefore.has(field),
143
+ });
144
+ }
145
+ for (const field of added) {
146
+ changes.push({
147
+ kind: 'added',
148
+ field,
149
+ to: after[field],
150
+ required: requiredAfter.has(field),
151
+ });
152
+ }
153
+
154
+ return { changes, ambiguous: candidates(removed, added, before, after) };
155
+ }
156
+ }
157
+
158
+ /**
159
+ * So the three axes write themselves under `x-fougere`, each by its own hand.
160
+ * FR : pour que les trois axes s'écrivent sous `x-fougere`, chacun de sa main.
161
+ * `ref(User)` → `{ role: { relation: { to: 'user', kind: 'one' } } }`
162
+ */
163
+ function describeExtension(field: Field, key: string): FieldExtension | undefined {
164
+ const extension: Record<string, unknown> = {};
165
+ for (const axis of EXTENSION_AXES) {
166
+ const declared = (field as unknown as Record<string, unknown>)[axis.slot];
167
+ if (declared === undefined) continue;
168
+ const wire = axis.describe(declared, key);
169
+ if (wire !== undefined) extension[axis.slot] = wire;
170
+ }
171
+ clean(extension);
172
+ return Object.keys(extension).length ? (extension as FieldExtension) : undefined;
173
+ }
174
+
175
+ /**
176
+ * So the shape stays plain JSON Schema, and what is ours sits under one key.
177
+ * FR : pour que la forme reste du JSON Schema, ce qui est à nous tenant sous une clé.
178
+ * `text({ max: 200 })` → `{ type: 'string', maxLength: 200 }`
179
+ */
180
+ function describeField(field: Field, key: string): FieldDescriptor {
181
+ // The shape IS JSON Schema, so it lands whole rather than key by key — which is what let
182
+ // it be copied under a computed key into a type that names its own.
183
+ const descriptor: FieldDescriptor = clean({ ...field.shape }) as FieldDescriptor;
184
+ if (field.meta?.description) descriptor.description = field.meta.description;
185
+ const extension = describeExtension(field, key);
186
+ if (extension) descriptor['x-fougere'] = extension;
187
+ return descriptor;
188
+ }
189
+
190
+ /**
191
+ * So a derived card names its root and what became of each field.
192
+ * FR : pour qu'une carte dérivée nomme sa racine et le sort de chaque champ.
193
+ * `Post.pick('title')` → `{ from: 'Post', nameOf: { title: 'title' } }`
194
+ */
195
+ function originOf(schema: SchemaView): DerivedFrom | undefined {
196
+ const { derivation } = schema;
197
+ if (!derivation) return undefined;
198
+ const nameOf = Object.fromEntries(
199
+ Object.entries(derivation.nameOf).map(([key, value]) => [key, value ?? null]),
200
+ );
201
+ return { from: derivation.sourceName, nameOf };
202
+ }
203
+
204
+ /**
205
+ * So a shape read back is the shape that was written, and an unknown type is refused.
206
+ * FR : pour qu'une forme relue soit celle qui a été écrite, un type inconnu étant refusé.
207
+ * `{ type: 'string', maxLength: 200 }` → the same shape a `text({ max: 200 })` states
208
+ */
209
+ function reconstructShape(property: FieldDescriptor): Field['shape'] | undefined {
210
+ const types = Array.isArray(property.type) ? property.type : property.type ? [property.type] : [];
211
+ if (!types.some((type) => type !== 'null')) return undefined;
212
+
213
+ // `describeField` writes the shape whole, so it is read whole: a list of keywords here
214
+ // would be a second inventory, and the one that forgets a keyword loses it in silence.
215
+ const { 'x-fougere': _extension, description: _description, ...shape } = property;
216
+ if (shape.items) shape.items = reconstructShape(shape.items) as FieldDescriptor;
217
+
218
+ return clean(shape) as Field['shape'];
219
+ }
220
+
221
+ /**
222
+ * So a field read back carries its axes, rebuilt by the axes themselves.
223
+ * FR : pour qu'un champ relu porte ses axes, reconstruits par les axes eux-mêmes.
224
+ * `{ 'x-fougere': { lifecycle: { create: 'now' } } }` → a field stamped at create
225
+ */
226
+ function reconstructField(
227
+ property: FieldDescriptor,
228
+ key: string,
229
+ resolve?: Resolver,
230
+ ): Field {
231
+ const shape = reconstructShape(property);
232
+ if (!shape) {
233
+ throw new Error(
234
+ `Field '${key}': the card carries no \`type\` for it, so there is no shape to rebuild. ` +
235
+ 'A field always states one.',
236
+ );
237
+ }
238
+ const extension = property['x-fougere'];
239
+ const axes: Record<string, unknown> = {};
240
+ for (const axis of EXTENSION_AXES) {
241
+ const wire = (extension as Record<string, unknown> | undefined)?.[axis.slot];
242
+ if (wire !== undefined) axes[axis.slot] = axis.reconstruct(wire, resolve);
243
+ }
244
+ return new Field({
245
+ shape,
246
+ ...axes,
247
+ meta:
248
+ property.description !== undefined
249
+ ? { description: property.description }
250
+ : undefined,
251
+ } as never);
252
+ }
253
+
254
+ /**
255
+ * So an axis that changed is one named difference, not a whole field marked dirty.
256
+ * FR : pour qu'un axe modifié soit une différence nommée, pas un champ entier marqué.
257
+ * `{ kind: 'restated', field: 'body', axis: 'boundary', … }`
258
+ */
259
+ function restated(
260
+ field: string,
261
+ before: FieldExtension | undefined,
262
+ after: FieldExtension | undefined,
263
+ ): Change[] {
264
+ return EXTENSION_SLOTS.filter((axis) => !dequal(before?.[axis], after?.[axis])).map(
265
+ (axis) =>
266
+ ({
267
+ kind: 'restated',
268
+ field,
269
+ axis,
270
+ from: before?.[axis],
271
+ to: after?.[axis],
272
+ }) as Change,
273
+ );
274
+ }
275
+
276
+ /**
277
+ * So two shapes are compared without what is not shape getting in the way.
278
+ * FR : pour que deux formes se comparent sans que le reste s'en mêle.
279
+ * `{ type: 'string', description: 'x' }` → `{ type: 'string' }`
280
+ */
281
+ function shapeOf(descriptor: FieldDescriptor): Record<string, unknown> {
282
+ const { 'x-fougere': _extension, description: _description, ...shape } = descriptor;
283
+ return shape as Record<string, unknown>;
284
+ }
285
+
286
+ /**
287
+ * So `['string','null']` and `['null','string']` are the same type, not a change.
288
+ * FR : pour que `['string','null']` et `['null','string']` soient un même type.
289
+ * `typesOf({ type: ['null', 'string'] })` → `['null', 'string']`
290
+ */
291
+ function typesOf(descriptor: FieldDescriptor): TypeSet {
292
+ const type = descriptor.type;
293
+ if (type === undefined) return [];
294
+ return (Array.isArray(type) ? [...type] : [type]).sort();
295
+ }
296
+
297
+ /**
298
+ * So a bound that moved is a `reshaped`, told apart from a type that changed.
299
+ * FR : pour qu'une borne déplacée soit un `reshaped`, distinct d'un type changé.
300
+ * `maxLength: 200` → `maxLength: 100` → one `reshaped`, never a `retyped`
301
+ */
302
+ function boundsOf(descriptor: FieldDescriptor): Record<string, unknown> {
303
+ const { type: _type, ...rest } = shapeOf(descriptor);
304
+ return rest;
305
+ }
306
+
307
+ /**
308
+ * So a removal plus an addition of the same shape is a question, never a guess.
309
+ * FR : pour qu'une suppression plus un ajout de même forme soit une question, pas un pari.
310
+ * `body` gone, `content` appeared → `ambiguous: [{ removed: 'body', added: 'content' }]`
311
+ */
312
+ function candidates(
313
+ removed: string[],
314
+ added: string[],
315
+ before: Record<string, FieldDescriptor>,
316
+ after: Record<string, FieldDescriptor>,
317
+ ): RenameCandidate[] {
318
+ const found: RenameCandidate[] = [];
319
+ for (const gone of removed) {
320
+ for (const appeared of added) {
321
+ if (dequal(shapeOf(before[gone]), shapeOf(after[appeared])))
322
+ found.push({ removed: gone, added: appeared });
323
+ }
324
+ }
325
+ const was = Object.keys(before);
326
+ const now = Object.keys(after);
327
+ const apart = ({ removed: gone, added: appeared }: RenameCandidate): number =>
328
+ Math.abs(now.indexOf(appeared) - was.indexOf(gone));
329
+ return found.sort((a, b) => apart(a) - apart(b));
330
+ }
331
+
332
+ /**
333
+ * So a group spanning several fields reaches a reader that only ever sees one field.
334
+ * FR : pour qu'un groupe couvrant plusieurs champs atteigne un lecteur qui n'en voit qu'un.
335
+ * `carryGroup(properties.listId, ['listId', 'docId'])` → the pair lands under its `role`
336
+ */
337
+ function carryGroup(property: FieldDescriptor | undefined, group: readonly string[]): void {
338
+ if (!property) return;
339
+ const extension = (property['x-fougere'] ??= {}) as { role?: { unique?: string[][] } };
340
+ const role = (extension.role ??= {});
341
+ (role.unique ??= []).push([...group]);
342
+ }
@@ -1,8 +1,8 @@
1
- import type { BoundaryRef } from '../axis/boundary/Boundary.js';
2
- import type { LifecycleRules } from '../axis/lifecycle/Lifecycle.js';
3
- import type { Relation } from '../axis/role/Relation.js';
4
- import type { RoleRules } from '../axis/role/Role.js';
5
- import type { Shape } from '../axis/shape/Shape.js';
1
+ import type { BoundaryRef } from '../../axis/boundary/Boundary.js';
2
+ import type { LifecycleRules } from '../../axis/lifecycle/Lifecycle.js';
3
+ import type { Relation } from '../../axis/role/Relation.js';
4
+ import type { RoleRules } from '../../axis/role/Role.js';
5
+ import type { Shape } from '../../axis/shape/Shape.js';
6
6
 
7
7
  type JsonSchemaType =
8
8
  | 'string'
@@ -40,7 +40,7 @@ export type RoleDescriptor = Pick<RoleRules, 'primary' | 'index'> & {
40
40
  export type RelationDescriptor = Pick<Relation, 'kind' | 'onDelete'> & { to: string };
41
41
 
42
42
  /**
43
- * What a derivation was cut from, and what the cut left — `survived` keyed by the
43
+ * What a derivation was cut from, and what the cut left — `nameOf` keyed by the
44
44
  * ORIGIN's field names. Absent on a declaration that derives from nothing.
45
45
  *
46
46
  * A dropped field is `null` and never `undefined`: JSON.stringify erases the second,
@@ -48,7 +48,7 @@ export type RelationDescriptor = Pick<Relation, 'kind' | 'onDelete'> & { to: str
48
48
  */
49
49
  export interface DerivedFrom {
50
50
  from: string;
51
- survived: Record<string, string | null>;
51
+ nameOf: Record<string, string | null>;
52
52
  }
53
53
 
54
54
  export interface SchemaDescriptor {
@@ -0,0 +1,40 @@
1
+ import type { ValidationError } from '../../validation.js';
2
+
3
+ /**
4
+ * So a card that cannot be read says what is wrong and what to write instead.
5
+ * FR : pour qu'une carte illisible dise ce qui cloche et quoi écrire.
6
+ * `refuse('role.relation.kind is "maybe"', 'Expected one of one, many.')`
7
+ */
8
+ export function refuse(what: string, fix: string): never {
9
+ throw new Error(`This card cannot be read: ${what}.\n ${fix}`);
10
+ }
11
+
12
+ /**
13
+ * So a card's axis passes the same validator as a hand-written one, and no second validator exists.
14
+ * FR : pour qu'un axe d'une carte passe le juge des autres, sans second juge.
15
+ * `{ update: 'maybe' }` on a card → `lifecycle is malformed — lifecycle.update: Expected 'now' or 'forbidden'`
16
+ */
17
+ export function admitWire(
18
+ validator: (value: unknown, errors: ValidationError[]) => void,
19
+ value: unknown,
20
+ slot: string,
21
+ ): void {
22
+ const errors: ValidationError[] = [];
23
+ validator(value, errors);
24
+ if (errors.length) {
25
+ refuse(
26
+ `${slot} is malformed — ${errors.map((e) => `${e.path}: ${e.message}`).join('; ')}`,
27
+ 'A card states an axis the way a declaration does.',
28
+ );
29
+ }
30
+ }
31
+
32
+ /**
33
+ * So a closed set is checked against the list that declares it, never against a copy.
34
+ * FR : pour qu'un ensemble fermé soit vérifié contre la liste qui le déclare.
35
+ * `oneOfTokens('many', RELATION_KINDS)` → `true`
36
+ */
37
+ export const oneOfTokens = <T extends readonly string[]>(
38
+ value: unknown,
39
+ tokens: T,
40
+ ): value is T[number] => typeof value === 'string' && (tokens as readonly string[]).includes(value);
@@ -1,5 +1,5 @@
1
- import type { BoundaryRef } from '../axis/boundary/Boundary.js';
2
- import type { LifecycleRules } from '../axis/lifecycle/Lifecycle.js';
1
+ import type { BoundaryRef } from '../../axis/boundary/Boundary.js';
2
+ import type { LifecycleRules } from '../../axis/lifecycle/Lifecycle.js';
3
3
  import type { FieldDescriptor, RoleDescriptor } from './Descriptor.js';
4
4
 
5
5
  /** One named difference, at one place. Each kind exists because a reader asks for it. */
@@ -8,7 +8,7 @@ export type Change =
8
8
  | { kind: 'added'; field: string; to: FieldDescriptor; required: boolean }
9
9
  /** DDL: drop the column. Codec: the old caller still sends it, and it goes nowhere. */
10
10
  | { kind: 'removed'; field: string; from: FieldDescriptor; required: boolean }
11
- /** DDL: rename the column. Codec and ORM: one more entry in the field-to-column map. */
11
+ /** DDL: rename the column. Codec and storage: one more entry in the field-to-column map. */
12
12
  | { kind: 'renamed'; from: string; to: string; field: FieldDescriptor }
13
13
  /** DDL: alter the type. Codec: convert the value, when it can. */
14
14
  | { kind: 'retyped'; field: string; from: TypeSet; to: TypeSet }
@@ -0,0 +1,40 @@
1
+ import { Validator } from '@cfworker/json-schema';
2
+ import type { Shape } from '../axis/shape/Shape.js';
3
+ import { isObject } from '../lib/utils.js';
4
+
5
+ /**
6
+ * The format is DATA, not a TypeScript interface: an interface is erased before a JS
7
+ * caller, a config or a card from another language could be measured against it.
8
+ */
9
+ export class AdapterFieldValidator {
10
+ private constructor(private readonly validator: Validator) {}
11
+
12
+ /** Compiled once, at the adapter's own module load. */
13
+ static of(format: Shape): AdapterFieldValidator {
14
+ return new AdapterFieldValidator(new Validator(format as object, '2020-12', true));
15
+ }
16
+
17
+ /** `Post.adapters.sql.body.columnType: Property "postgre" does not match …` */
18
+ assert(entries: unknown, path: string): void {
19
+ if (entries === undefined) return;
20
+
21
+ if (!isObject(entries)) {
22
+ throw new Error(
23
+ `${path}: expected an object keyed by field name, got ${typeof entries}.`,
24
+ );
25
+ }
26
+
27
+ for (const [field, entry] of Object.entries(entries)) {
28
+ const verdict = this.validator.validate(entry);
29
+ if (verdict.valid) continue;
30
+
31
+ // The engine reports outermost first and ends on `False boolean schema`, which names
32
+ // nothing. The last unit above that one is the one carrying the offending key.
33
+ const units = verdict.errors.filter((unit) => unit.keyword !== 'false');
34
+ const failure = units[units.length - 1] ?? verdict.errors[0];
35
+ const nested = (failure?.instanceLocation ?? '#').slice(1).replaceAll('/', '.');
36
+
37
+ throw new Error(`${path}.${field}${nested}: ${failure?.error ?? 'does not match the format'}`);
38
+ }
39
+ }
40
+ }
@@ -1,14 +1,14 @@
1
- import { EXTENSION_AXES } from "../axis/Axis.js";
2
- import { Anatomy } from "../axis/shape/Shape.js";
3
- import type { Field } from "../fields/Field.js";
4
- import { isObject } from "./ValueForm.js";
5
- import type { ValidationError, ValidationResult } from "./result.js";
1
+ import { EXTENSION_AXES } from '../axis/Axis.js';
2
+ import { Shapes } from '../axis/shape/Shape.js';
3
+ import type { Field } from '../field/Field.js';
4
+ import { isObject } from '../lib/utils.js';
5
+ import type { ValidationError, ValidationResult } from '../validation.js';
6
6
 
7
- export class FieldJudge {
7
+ export class FieldDeclarationValidator {
8
8
  private constructor(private readonly declaration: unknown) {}
9
9
 
10
- static of(declaration: unknown): FieldJudge {
11
- return new FieldJudge(declaration);
10
+ static of(declaration: unknown): FieldDeclarationValidator {
11
+ return new FieldDeclarationValidator(declaration);
12
12
  }
13
13
 
14
14
  get verdict(): ValidationResult<Field> {
@@ -19,7 +19,7 @@ export class FieldJudge {
19
19
  success: false,
20
20
  errors: [
21
21
  {
22
- path: ".",
22
+ path: '.',
23
23
  message: `Expected an object — got ${JSON.stringify(declaration)}`,
24
24
  },
25
25
  ],
@@ -28,29 +28,29 @@ export class FieldJudge {
28
28
 
29
29
  const errors: ValidationError[] = [];
30
30
 
31
- if (!Anatomy.is(declaration.shape)) {
31
+ if (!Shapes.is(declaration.shape)) {
32
32
  errors.push({
33
- path: "shape",
33
+ path: 'shape',
34
34
  message: `Every field states a shape — got ${JSON.stringify(declaration.shape)}`,
35
35
  });
36
36
  }
37
37
 
38
38
  for (const axis of EXTENSION_AXES) {
39
39
  const declared = declaration[axis.slot];
40
- if (declared !== undefined) axis.judge(declared, errors);
40
+ if (declared !== undefined) axis.validator(declared, errors);
41
41
  }
42
42
 
43
43
  if (declaration.meta !== undefined) {
44
44
  if (!isObject(declaration.meta)) {
45
45
  errors.push({
46
- path: "meta",
46
+ path: 'meta',
47
47
  message: `Expected an object — got ${JSON.stringify(declaration.meta)}`,
48
48
  });
49
49
  } else if (
50
50
  declaration.meta.description !== undefined &&
51
- typeof declaration.meta.description !== "string"
51
+ typeof declaration.meta.description !== 'string'
52
52
  ) {
53
- errors.push({ path: "meta.description", message: "Expected a string" });
53
+ errors.push({ path: 'meta.description', message: 'Expected a string' });
54
54
  }
55
55
  }
56
56