@metaobjectsdev/metadata 0.5.0-rc.1

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 (405) hide show
  1. package/LICENSE +189 -0
  2. package/README.md +29 -0
  3. package/dist/attr-class-map.d.ts +26 -0
  4. package/dist/attr-class-map.d.ts.map +1 -0
  5. package/dist/attr-class-map.js +44 -0
  6. package/dist/attr-class-map.js.map +1 -0
  7. package/dist/attr-schema-validate.d.ts +9 -0
  8. package/dist/attr-schema-validate.d.ts.map +1 -0
  9. package/dist/attr-schema-validate.js +100 -0
  10. package/dist/attr-schema-validate.js.map +1 -0
  11. package/dist/constants.d.ts +208 -0
  12. package/dist/constants.d.ts.map +1 -0
  13. package/dist/constants.js +419 -0
  14. package/dist/constants.js.map +1 -0
  15. package/dist/core/attr/attr-constants.d.ts +12 -0
  16. package/dist/core/attr/attr-constants.d.ts.map +1 -0
  17. package/dist/core/attr/attr-constants.js +27 -0
  18. package/dist/core/attr/attr-constants.js.map +1 -0
  19. package/dist/core/attr/meta-attr-filter.d.ts +10 -0
  20. package/dist/core/attr/meta-attr-filter.d.ts.map +1 -0
  21. package/dist/core/attr/meta-attr-filter.js +56 -0
  22. package/dist/core/attr/meta-attr-filter.js.map +1 -0
  23. package/dist/core/attr/meta-attr-properties.d.ts +9 -0
  24. package/dist/core/attr/meta-attr-properties.d.ts.map +1 -0
  25. package/dist/core/attr/meta-attr-properties.js +22 -0
  26. package/dist/core/attr/meta-attr-properties.js.map +1 -0
  27. package/dist/core/attr/meta-attr-stringarray.d.ts +9 -0
  28. package/dist/core/attr/meta-attr-stringarray.d.ts.map +1 -0
  29. package/dist/core/attr/meta-attr-stringarray.js +29 -0
  30. package/dist/core/attr/meta-attr-stringarray.js.map +1 -0
  31. package/dist/core/attr/meta-attr.d.ts +37 -0
  32. package/dist/core/attr/meta-attr.d.ts.map +1 -0
  33. package/dist/core/attr/meta-attr.js +97 -0
  34. package/dist/core/attr/meta-attr.js.map +1 -0
  35. package/dist/core/export-json.d.ts +29 -0
  36. package/dist/core/export-json.d.ts.map +1 -0
  37. package/dist/core/export-json.js +45 -0
  38. package/dist/core/export-json.js.map +1 -0
  39. package/dist/core/field/field-constants.d.ts +40 -0
  40. package/dist/core/field/field-constants.d.ts.map +1 -0
  41. package/dist/core/field/field-constants.js +66 -0
  42. package/dist/core/field/field-constants.js.map +1 -0
  43. package/dist/core/field/field-schema.d.ts +6 -0
  44. package/dist/core/field/field-schema.d.ts.map +1 -0
  45. package/dist/core/field/field-schema.js +92 -0
  46. package/dist/core/field/field-schema.js.map +1 -0
  47. package/dist/core/field/meta-field.d.ts +50 -0
  48. package/dist/core/field/meta-field.d.ts.map +1 -0
  49. package/dist/core/field/meta-field.js +121 -0
  50. package/dist/core/field/meta-field.js.map +1 -0
  51. package/dist/core/file-meta-data-loader.d.ts +18 -0
  52. package/dist/core/file-meta-data-loader.d.ts.map +1 -0
  53. package/dist/core/file-meta-data-loader.js +81 -0
  54. package/dist/core/file-meta-data-loader.js.map +1 -0
  55. package/dist/core/file-source.d.ts +12 -0
  56. package/dist/core/file-source.d.ts.map +1 -0
  57. package/dist/core/file-source.js +46 -0
  58. package/dist/core/file-source.js.map +1 -0
  59. package/dist/core/identity/identity-constants.d.ts +19 -0
  60. package/dist/core/identity/identity-constants.d.ts.map +1 -0
  61. package/dist/core/identity/identity-constants.js +35 -0
  62. package/dist/core/identity/identity-constants.js.map +1 -0
  63. package/dist/core/identity/identity-schema.d.ts +6 -0
  64. package/dist/core/identity/identity-schema.d.ts.map +1 -0
  65. package/dist/core/identity/identity-schema.js +55 -0
  66. package/dist/core/identity/identity-schema.js.map +1 -0
  67. package/dist/core/identity/meta-identity.d.ts +71 -0
  68. package/dist/core/identity/meta-identity.d.ts.map +1 -0
  69. package/dist/core/identity/meta-identity.js +129 -0
  70. package/dist/core/identity/meta-identity.js.map +1 -0
  71. package/dist/core/index.d.ts +6 -0
  72. package/dist/core/index.d.ts.map +1 -0
  73. package/dist/core/index.js +11 -0
  74. package/dist/core/index.js.map +1 -0
  75. package/dist/core/object/meta-object.d.ts +39 -0
  76. package/dist/core/object/meta-object.d.ts.map +1 -0
  77. package/dist/core/object/meta-object.js +80 -0
  78. package/dist/core/object/meta-object.js.map +1 -0
  79. package/dist/core/object/object-constants.d.ts +5 -0
  80. package/dist/core/object/object-constants.d.ts.map +1 -0
  81. package/dist/core/object/object-constants.js +18 -0
  82. package/dist/core/object/object-constants.js.map +1 -0
  83. package/dist/core/object/object-schema.d.ts +4 -0
  84. package/dist/core/object/object-schema.d.ts.map +1 -0
  85. package/dist/core/object/object-schema.js +5 -0
  86. package/dist/core/object/object-schema.js.map +1 -0
  87. package/dist/core/parser-yaml.d.ts +3 -0
  88. package/dist/core/parser-yaml.d.ts.map +1 -0
  89. package/dist/core/parser-yaml.js +39 -0
  90. package/dist/core/parser-yaml.js.map +1 -0
  91. package/dist/core/query/query-constants.d.ts +20 -0
  92. package/dist/core/query/query-constants.d.ts.map +1 -0
  93. package/dist/core/query/query-constants.js +56 -0
  94. package/dist/core/query/query-constants.js.map +1 -0
  95. package/dist/core/relationship/find-reference.d.ts +22 -0
  96. package/dist/core/relationship/find-reference.d.ts.map +1 -0
  97. package/dist/core/relationship/find-reference.js +29 -0
  98. package/dist/core/relationship/find-reference.js.map +1 -0
  99. package/dist/core/relationship/meta-relationship.d.ts +11 -0
  100. package/dist/core/relationship/meta-relationship.d.ts.map +1 -0
  101. package/dist/core/relationship/meta-relationship.js +27 -0
  102. package/dist/core/relationship/meta-relationship.js.map +1 -0
  103. package/dist/core/relationship/relationship-constants.d.ts +14 -0
  104. package/dist/core/relationship/relationship-constants.d.ts.map +1 -0
  105. package/dist/core/relationship/relationship-constants.js +28 -0
  106. package/dist/core/relationship/relationship-constants.js.map +1 -0
  107. package/dist/core/relationship/relationship-schema.d.ts +4 -0
  108. package/dist/core/relationship/relationship-schema.d.ts.map +1 -0
  109. package/dist/core/relationship/relationship-schema.js +37 -0
  110. package/dist/core/relationship/relationship-schema.js.map +1 -0
  111. package/dist/core/validator/meta-validator.d.ts +29 -0
  112. package/dist/core/validator/meta-validator.d.ts.map +1 -0
  113. package/dist/core/validator/meta-validator.js +49 -0
  114. package/dist/core/validator/meta-validator.js.map +1 -0
  115. package/dist/core/validator/validator-constants.d.ts +11 -0
  116. package/dist/core/validator/validator-constants.d.ts.map +1 -0
  117. package/dist/core/validator/validator-constants.js +25 -0
  118. package/dist/core/validator/validator-constants.js.map +1 -0
  119. package/dist/core/validator/validator-schema.d.ts +4 -0
  120. package/dist/core/validator/validator-schema.d.ts.map +1 -0
  121. package/dist/core/validator/validator-schema.js +38 -0
  122. package/dist/core/validator/validator-schema.js.map +1 -0
  123. package/dist/core/yaml-desugar.d.ts +10 -0
  124. package/dist/core/yaml-desugar.d.ts.map +1 -0
  125. package/dist/core/yaml-desugar.js +99 -0
  126. package/dist/core/yaml-desugar.js.map +1 -0
  127. package/dist/core-attr-schemas.d.ts +22 -0
  128. package/dist/core-attr-schemas.d.ts.map +1 -0
  129. package/dist/core-attr-schemas.js +324 -0
  130. package/dist/core-attr-schemas.js.map +1 -0
  131. package/dist/core-types.d.ts +20 -0
  132. package/dist/core-types.d.ts.map +1 -0
  133. package/dist/core-types.js +225 -0
  134. package/dist/core-types.js.map +1 -0
  135. package/dist/data-converter.d.ts +17 -0
  136. package/dist/data-converter.d.ts.map +1 -0
  137. package/dist/data-converter.js +117 -0
  138. package/dist/data-converter.js.map +1 -0
  139. package/dist/data-type.d.ts +15 -0
  140. package/dist/data-type.d.ts.map +1 -0
  141. package/dist/data-type.js +25 -0
  142. package/dist/data-type.js.map +1 -0
  143. package/dist/db/db-attr-schemas.d.ts +8 -0
  144. package/dist/db/db-attr-schemas.d.ts.map +1 -0
  145. package/dist/db/db-attr-schemas.js +26 -0
  146. package/dist/db/db-attr-schemas.js.map +1 -0
  147. package/dist/db/db-provider.d.ts +3 -0
  148. package/dist/db/db-provider.d.ts.map +1 -0
  149. package/dist/db/db-provider.js +28 -0
  150. package/dist/db/db-provider.js.map +1 -0
  151. package/dist/errors.d.ts +26 -0
  152. package/dist/errors.d.ts.map +1 -0
  153. package/dist/errors.js +59 -0
  154. package/dist/errors.js.map +1 -0
  155. package/dist/index.d.ts +70 -0
  156. package/dist/index.d.ts.map +1 -0
  157. package/dist/index.js +76 -0
  158. package/dist/index.js.map +1 -0
  159. package/dist/loader/meta-data-loader.d.ts +87 -0
  160. package/dist/loader/meta-data-loader.d.ts.map +1 -0
  161. package/dist/loader/meta-data-loader.js +232 -0
  162. package/dist/loader/meta-data-loader.js.map +1 -0
  163. package/dist/loader/meta-data-source.d.ts +23 -0
  164. package/dist/loader/meta-data-source.d.ts.map +1 -0
  165. package/dist/loader/meta-data-source.js +20 -0
  166. package/dist/loader/meta-data-source.js.map +1 -0
  167. package/dist/loader/validation-passes.d.ts +7 -0
  168. package/dist/loader/validation-passes.d.ts.map +1 -0
  169. package/dist/loader/validation-passes.js +244 -0
  170. package/dist/loader/validation-passes.js.map +1 -0
  171. package/dist/meta/find-reference.d.ts +22 -0
  172. package/dist/meta/find-reference.d.ts.map +1 -0
  173. package/dist/meta/find-reference.js +29 -0
  174. package/dist/meta/find-reference.js.map +1 -0
  175. package/dist/meta/meta-attr.d.ts +8 -0
  176. package/dist/meta/meta-attr.d.ts.map +1 -0
  177. package/dist/meta/meta-attr.js +17 -0
  178. package/dist/meta/meta-attr.js.map +1 -0
  179. package/dist/meta/meta-data.d.ts +107 -0
  180. package/dist/meta/meta-data.d.ts.map +1 -0
  181. package/dist/meta/meta-data.js +302 -0
  182. package/dist/meta/meta-data.js.map +1 -0
  183. package/dist/meta/meta-field.d.ts +48 -0
  184. package/dist/meta/meta-field.d.ts.map +1 -0
  185. package/dist/meta/meta-field.js +94 -0
  186. package/dist/meta/meta-field.js.map +1 -0
  187. package/dist/meta/meta-identity.d.ts +71 -0
  188. package/dist/meta/meta-identity.d.ts.map +1 -0
  189. package/dist/meta/meta-identity.js +129 -0
  190. package/dist/meta/meta-identity.js.map +1 -0
  191. package/dist/meta/meta-layout.d.ts +23 -0
  192. package/dist/meta/meta-layout.d.ts.map +1 -0
  193. package/dist/meta/meta-layout.js +45 -0
  194. package/dist/meta/meta-layout.js.map +1 -0
  195. package/dist/meta/meta-object.d.ts +40 -0
  196. package/dist/meta/meta-object.d.ts.map +1 -0
  197. package/dist/meta/meta-object.js +81 -0
  198. package/dist/meta/meta-object.js.map +1 -0
  199. package/dist/meta/meta-origin.d.ts +32 -0
  200. package/dist/meta/meta-origin.d.ts.map +1 -0
  201. package/dist/meta/meta-origin.js +55 -0
  202. package/dist/meta/meta-origin.js.map +1 -0
  203. package/dist/meta/meta-relationship.d.ts +11 -0
  204. package/dist/meta/meta-relationship.d.ts.map +1 -0
  205. package/dist/meta/meta-relationship.js +27 -0
  206. package/dist/meta/meta-relationship.js.map +1 -0
  207. package/dist/meta/meta-root.d.ts +12 -0
  208. package/dist/meta/meta-root.d.ts.map +1 -0
  209. package/dist/meta/meta-root.js +24 -0
  210. package/dist/meta/meta-root.js.map +1 -0
  211. package/dist/meta/meta-source.d.ts +18 -0
  212. package/dist/meta/meta-source.d.ts.map +1 -0
  213. package/dist/meta/meta-source.js +31 -0
  214. package/dist/meta/meta-source.js.map +1 -0
  215. package/dist/meta/meta-validator.d.ts +29 -0
  216. package/dist/meta/meta-validator.d.ts.map +1 -0
  217. package/dist/meta/meta-validator.js +49 -0
  218. package/dist/meta/meta-validator.js.map +1 -0
  219. package/dist/meta/meta-view.d.ts +4 -0
  220. package/dist/meta/meta-view.d.ts.map +1 -0
  221. package/dist/meta/meta-view.js +8 -0
  222. package/dist/meta/meta-view.js.map +1 -0
  223. package/dist/naming.d.ts +27 -0
  224. package/dist/naming.d.ts.map +1 -0
  225. package/dist/naming.js +72 -0
  226. package/dist/naming.js.map +1 -0
  227. package/dist/object-serializer.d.ts +10 -0
  228. package/dist/object-serializer.d.ts.map +1 -0
  229. package/dist/object-serializer.js +128 -0
  230. package/dist/object-serializer.js.map +1 -0
  231. package/dist/overlay.d.ts +2 -0
  232. package/dist/overlay.d.ts.map +1 -0
  233. package/dist/overlay.js +6 -0
  234. package/dist/overlay.js.map +1 -0
  235. package/dist/parser-core.d.ts +47 -0
  236. package/dist/parser-core.d.ts.map +1 -0
  237. package/dist/parser-core.js +516 -0
  238. package/dist/parser-core.js.map +1 -0
  239. package/dist/parser-json.d.ts +4 -0
  240. package/dist/parser-json.d.ts.map +1 -0
  241. package/dist/parser-json.js +19 -0
  242. package/dist/parser-json.js.map +1 -0
  243. package/dist/persistence/db/db-attr-schemas.d.ts +8 -0
  244. package/dist/persistence/db/db-attr-schemas.d.ts.map +1 -0
  245. package/dist/persistence/db/db-attr-schemas.js +28 -0
  246. package/dist/persistence/db/db-attr-schemas.js.map +1 -0
  247. package/dist/persistence/db/db-constants.d.ts +5 -0
  248. package/dist/persistence/db/db-constants.d.ts.map +1 -0
  249. package/dist/persistence/db/db-constants.js +6 -0
  250. package/dist/persistence/db/db-constants.js.map +1 -0
  251. package/dist/persistence/db/db-provider.d.ts +3 -0
  252. package/dist/persistence/db/db-provider.d.ts.map +1 -0
  253. package/dist/persistence/db/db-provider.js +29 -0
  254. package/dist/persistence/db/db-provider.js.map +1 -0
  255. package/dist/persistence/db/db-schema.d.ts +8 -0
  256. package/dist/persistence/db/db-schema.d.ts.map +1 -0
  257. package/dist/persistence/db/db-schema.js +27 -0
  258. package/dist/persistence/db/db-schema.js.map +1 -0
  259. package/dist/persistence/origin/meta-origin.d.ts +32 -0
  260. package/dist/persistence/origin/meta-origin.d.ts.map +1 -0
  261. package/dist/persistence/origin/meta-origin.js +55 -0
  262. package/dist/persistence/origin/meta-origin.js.map +1 -0
  263. package/dist/persistence/origin/origin-constants.d.ts +12 -0
  264. package/dist/persistence/origin/origin-constants.d.ts.map +1 -0
  265. package/dist/persistence/origin/origin-constants.js +27 -0
  266. package/dist/persistence/origin/origin-constants.js.map +1 -0
  267. package/dist/persistence/origin/origin-schema.d.ts +4 -0
  268. package/dist/persistence/origin/origin-schema.d.ts.map +1 -0
  269. package/dist/persistence/origin/origin-schema.js +49 -0
  270. package/dist/persistence/origin/origin-schema.js.map +1 -0
  271. package/dist/persistence/source/meta-source.d.ts +18 -0
  272. package/dist/persistence/source/meta-source.d.ts.map +1 -0
  273. package/dist/persistence/source/meta-source.js +31 -0
  274. package/dist/persistence/source/meta-source.js.map +1 -0
  275. package/dist/persistence/source/source-constants.d.ts +16 -0
  276. package/dist/persistence/source/source-constants.d.ts.map +1 -0
  277. package/dist/persistence/source/source-constants.js +28 -0
  278. package/dist/persistence/source/source-constants.js.map +1 -0
  279. package/dist/presentation/layout/layout-constants.d.ts +10 -0
  280. package/dist/presentation/layout/layout-constants.d.ts.map +1 -0
  281. package/dist/presentation/layout/layout-constants.js +21 -0
  282. package/dist/presentation/layout/layout-constants.js.map +1 -0
  283. package/dist/presentation/layout/layout-schema.d.ts +4 -0
  284. package/dist/presentation/layout/layout-schema.d.ts.map +1 -0
  285. package/dist/presentation/layout/layout-schema.js +46 -0
  286. package/dist/presentation/layout/layout-schema.js.map +1 -0
  287. package/dist/presentation/layout/meta-layout.d.ts +23 -0
  288. package/dist/presentation/layout/meta-layout.d.ts.map +1 -0
  289. package/dist/presentation/layout/meta-layout.js +47 -0
  290. package/dist/presentation/layout/meta-layout.js.map +1 -0
  291. package/dist/presentation/view/meta-view.d.ts +4 -0
  292. package/dist/presentation/view/meta-view.d.ts.map +1 -0
  293. package/dist/presentation/view/meta-view.js +8 -0
  294. package/dist/presentation/view/meta-view.js.map +1 -0
  295. package/dist/presentation/view/view-constants.d.ts +20 -0
  296. package/dist/presentation/view/view-constants.d.ts.map +1 -0
  297. package/dist/presentation/view/view-constants.js +47 -0
  298. package/dist/presentation/view/view-constants.js.map +1 -0
  299. package/dist/presentation/view/view-schema.d.ts +4 -0
  300. package/dist/presentation/view/view-schema.d.ts.map +1 -0
  301. package/dist/presentation/view/view-schema.js +15 -0
  302. package/dist/presentation/view/view-schema.js.map +1 -0
  303. package/dist/provider.d.ts +20 -0
  304. package/dist/provider.d.ts.map +1 -0
  305. package/dist/provider.js +58 -0
  306. package/dist/provider.js.map +1 -0
  307. package/dist/registry.d.ts +89 -0
  308. package/dist/registry.d.ts.map +1 -0
  309. package/dist/registry.js +107 -0
  310. package/dist/registry.js.map +1 -0
  311. package/dist/serializer-json.d.ts +16 -0
  312. package/dist/serializer-json.d.ts.map +1 -0
  313. package/dist/serializer-json.js +154 -0
  314. package/dist/serializer-json.js.map +1 -0
  315. package/dist/shared/base-types.d.ts +23 -0
  316. package/dist/shared/base-types.d.ts.map +1 -0
  317. package/dist/shared/base-types.js +43 -0
  318. package/dist/shared/base-types.js.map +1 -0
  319. package/dist/shared/meta-data.d.ts +123 -0
  320. package/dist/shared/meta-data.d.ts.map +1 -0
  321. package/dist/shared/meta-data.js +365 -0
  322. package/dist/shared/meta-data.js.map +1 -0
  323. package/dist/shared/meta-root.d.ts +12 -0
  324. package/dist/shared/meta-root.d.ts.map +1 -0
  325. package/dist/shared/meta-root.js +24 -0
  326. package/dist/shared/meta-root.js.map +1 -0
  327. package/dist/shared/structural.d.ts +20 -0
  328. package/dist/shared/structural.d.ts.map +1 -0
  329. package/dist/shared/structural.js +49 -0
  330. package/dist/shared/structural.js.map +1 -0
  331. package/dist/subtype-rules.d.ts +8 -0
  332. package/dist/subtype-rules.d.ts.map +1 -0
  333. package/dist/subtype-rules.js +34 -0
  334. package/dist/subtype-rules.js.map +1 -0
  335. package/dist/super-resolve.d.ts +34 -0
  336. package/dist/super-resolve.d.ts.map +1 -0
  337. package/dist/super-resolve.js +124 -0
  338. package/dist/super-resolve.js.map +1 -0
  339. package/package.json +50 -0
  340. package/src/attr-class-map.ts +64 -0
  341. package/src/attr-schema-validate.ts +134 -0
  342. package/src/core/attr/attr-constants.ts +31 -0
  343. package/src/core/attr/meta-attr-filter.ts +67 -0
  344. package/src/core/attr/meta-attr-properties.ts +26 -0
  345. package/src/core/attr/meta-attr-stringarray.ts +31 -0
  346. package/src/core/attr/meta-attr.ts +125 -0
  347. package/src/core/export-json.ts +66 -0
  348. package/src/core/field/field-constants.ts +79 -0
  349. package/src/core/field/field-schema.ts +121 -0
  350. package/src/core/field/meta-field.ts +179 -0
  351. package/src/core/file-meta-data-loader.ts +89 -0
  352. package/src/core/file-source.ts +52 -0
  353. package/src/core/identity/identity-constants.ts +44 -0
  354. package/src/core/identity/identity-schema.ts +80 -0
  355. package/src/core/identity/meta-identity.ts +148 -0
  356. package/src/core/index.ts +12 -0
  357. package/src/core/object/meta-object.ts +151 -0
  358. package/src/core/object/object-constants.ts +21 -0
  359. package/src/core/object/object-schema.ts +7 -0
  360. package/src/core/parser-yaml.ts +54 -0
  361. package/src/core/query/query-constants.ts +66 -0
  362. package/src/core/relationship/find-reference.ts +44 -0
  363. package/src/core/relationship/meta-relationship.ts +36 -0
  364. package/src/core/relationship/relationship-constants.ts +38 -0
  365. package/src/core/relationship/relationship-schema.ts +49 -0
  366. package/src/core/validator/meta-validator.ts +62 -0
  367. package/src/core/validator/validator-constants.ts +31 -0
  368. package/src/core/validator/validator-schema.ts +50 -0
  369. package/src/core/yaml-desugar.ts +145 -0
  370. package/src/core-types.ts +329 -0
  371. package/src/data-converter.ts +125 -0
  372. package/src/data-type.ts +33 -0
  373. package/src/errors.ts +68 -0
  374. package/src/index.ts +165 -0
  375. package/src/loader/meta-data-loader.ts +307 -0
  376. package/src/loader/meta-data-source.ts +35 -0
  377. package/src/loader/validation-passes.ts +370 -0
  378. package/src/naming.ts +86 -0
  379. package/src/object-serializer.ts +153 -0
  380. package/src/overlay.ts +5 -0
  381. package/src/parser-core.ts +815 -0
  382. package/src/parser-json.ts +28 -0
  383. package/src/persistence/db/db-constants.ts +6 -0
  384. package/src/persistence/db/db-provider.ts +36 -0
  385. package/src/persistence/db/db-schema.ts +40 -0
  386. package/src/persistence/origin/meta-origin.ts +67 -0
  387. package/src/persistence/origin/origin-constants.ts +35 -0
  388. package/src/persistence/origin/origin-schema.ts +66 -0
  389. package/src/persistence/source/meta-source.ts +38 -0
  390. package/src/persistence/source/source-constants.ts +35 -0
  391. package/src/presentation/layout/layout-constants.ts +27 -0
  392. package/src/presentation/layout/layout-schema.ts +62 -0
  393. package/src/presentation/layout/meta-layout.ts +61 -0
  394. package/src/presentation/view/meta-view.ts +8 -0
  395. package/src/presentation/view/view-constants.ts +53 -0
  396. package/src/presentation/view/view-schema.ts +21 -0
  397. package/src/provider.ts +85 -0
  398. package/src/registry.ts +190 -0
  399. package/src/serializer-json.ts +210 -0
  400. package/src/shared/base-types.ts +52 -0
  401. package/src/shared/meta-data.ts +443 -0
  402. package/src/shared/meta-root.ts +33 -0
  403. package/src/shared/structural.ts +62 -0
  404. package/src/subtype-rules.ts +56 -0
  405. package/src/super-resolve.ts +147 -0
@@ -0,0 +1,815 @@
1
+ // Shared canonical tree-builder — redesigned (post-v0.3) format.
2
+ //
3
+ // buildTree() turns a canonical-shaped JS object into a typed MetaData tree.
4
+ // parser-json.ts and parser-yaml.ts both funnel their parsed/desugared input
5
+ // through here so JSON and YAML stay isomorphic ("one structure, two
6
+ // renderings").
7
+ //
8
+ // Node encoding: every node is a single-key map { "<type>.<subType>": <body> }.
9
+ // The wrapper key FUSES type and subType into one dotted token (object.entity,
10
+ // field.long, metadata.root). The body is a map of reserved structural keys
11
+ // (name/package/extends/abstract/overlay/isArray/children), `@`-prefixed
12
+ // attributes, and the children list.
13
+ //
14
+ // Operator vocabulary (canonical, no aliases):
15
+ // - extends: string ref to a supertype (immediate resolution against
16
+ // the accumulating root, or deferred for cross-file refs)
17
+ // - overlay:true re-opens an existing same-(type,name) child; errors if missing
18
+ //
19
+ // Default (no operator): silently reuse existing same-(type,name) child, or
20
+ // create new.
21
+ //
22
+ // Context package for super resolution:
23
+ // Each node's effective package is its own package if set, or inherited from
24
+ // the nearest ancestor with a package.
25
+
26
+ import { TypeId, TypeRegistry } from "./registry.js";
27
+ import type { MetaData } from "./shared/meta-data.js";
28
+ import { MetaRoot } from "./shared/meta-root.js";
29
+ import { MetaAttr } from "./core/attr/meta-attr.js";
30
+ import { inferAttrSubType } from "./serializer-json.js";
31
+ import { ParseError, type ErrorCode } from "./errors.js";
32
+ import { resolveSuperRef } from "./super-resolve.js";
33
+ import {
34
+ TYPE_ATTR,
35
+ TYPE_FIELD,
36
+ TYPE_OBJECT,
37
+ TYPE_VALIDATOR,
38
+ SUBTYPE_BASE,
39
+ } from "./shared/base-types.js";
40
+ import {
41
+ RESERVED_KEYS,
42
+ RESERVED_KEY_NAME,
43
+ RESERVED_KEY_PACKAGE,
44
+ RESERVED_KEY_EXTENDS,
45
+ RESERVED_KEY_ABSTRACT,
46
+ RESERVED_KEY_OVERLAY,
47
+ RESERVED_KEY_IS_ARRAY,
48
+ RESERVED_KEY_CHILDREN,
49
+ RESERVED_KEY_VALUE,
50
+ JSON_KEY_SCHEMA,
51
+ ATTR_PREFIX,
52
+ TYPE_SUBTYPE_SEPARATOR,
53
+ PACKAGE_SEPARATOR,
54
+ } from "./shared/structural.js";
55
+ import { ATTR_SUBTYPE_PROPERTIES } from "./core/attr/attr-constants.js";
56
+ import type { AttrValue } from "./shared/meta-data.js";
57
+
58
+ // ---------------------------------------------------------------------------
59
+ // Public API
60
+ // ---------------------------------------------------------------------------
61
+
62
+ export interface ParseOptions {
63
+ registry: TypeRegistry; // required — types must be registered
64
+ strict?: boolean; // throw on parsing problems? default false
65
+ sourceName?: string; // for error messages, e.g., "fishstore.json"
66
+ /**
67
+ * Loader's accumulating root. If provided, parse merges nodes into it.
68
+ * If undefined, creates a new root from the JSON's root node.
69
+ *
70
+ * When provided, the returned `root` is this same instance (possibly mutated).
71
+ * When undefined, a fresh root is created from the JSON.
72
+ */
73
+ intoRoot?: MetaRoot;
74
+ /**
75
+ * If true, super refs that don't resolve at parse time are NOT a parse error;
76
+ * the model retains its raw `superRef` and a second pass (via
77
+ * resolveDeferredSupers in super-resolve.ts) is expected after all input
78
+ * files are parsed. Used by Loader.load/loadJsonStrings to support cross-file
79
+ * super resolution where one file may declare a super target that lives in
80
+ * another file parsed later.
81
+ */
82
+ deferSuperResolution?: boolean;
83
+ }
84
+
85
+ export interface ParseResult {
86
+ root: MetaRoot;
87
+ warnings: string[];
88
+ errors: ParseError[];
89
+ }
90
+
91
+ // ---------------------------------------------------------------------------
92
+ // Internal helper — build ParseError opts, omitting undefined fields
93
+ // (required by exactOptionalPropertyTypes: true in the project tsconfig)
94
+ // ---------------------------------------------------------------------------
95
+
96
+ export function errOpts(
97
+ source: string | undefined,
98
+ path?: string,
99
+ ): { source?: string; path?: string } {
100
+ const opts: { source?: string; path?: string } = {};
101
+ if (source !== undefined) opts.source = source;
102
+ if (path !== undefined) opts.path = path;
103
+ return opts;
104
+ }
105
+
106
+ // ---------------------------------------------------------------------------
107
+ // Internal helper — report a problem: throw in strict mode, push warning otherwise
108
+ // ---------------------------------------------------------------------------
109
+
110
+ function reportProblem(
111
+ msg: string,
112
+ strict: boolean,
113
+ warnings: string[],
114
+ source: string | undefined,
115
+ path: string,
116
+ code?: ErrorCode,
117
+ ): void {
118
+ if (strict) {
119
+ const opts = errOpts(source, path);
120
+ if (code !== undefined) throw new ParseError(msg, { ...opts, code });
121
+ else throw new ParseError(msg, opts);
122
+ }
123
+ warnings.push(msg);
124
+ }
125
+
126
+ // ---------------------------------------------------------------------------
127
+ // Internal helper — split a fused wrapper key into (type, subType).
128
+ //
129
+ // Canonical JSON always writes the full `type.subType`. An omitted subType
130
+ // (a bare `type` key, no `.`) resolves to the type's registry default — the
131
+ // first registered subtype, falling back to SUBTYPE_BASE.
132
+ // ---------------------------------------------------------------------------
133
+
134
+ interface SplitKey {
135
+ type: string;
136
+ subType: string;
137
+ /** true when the subType was fused into the key; false when defaulted. */
138
+ explicit: boolean;
139
+ }
140
+
141
+ function defaultSubTypeFor(type: string, registry: TypeRegistry): string {
142
+ const subs = registry.allSubTypesOf(type);
143
+ const candidate = subs.length > 0 ? subs[0]! : SUBTYPE_BASE;
144
+ if (!registry.has(type, candidate) && registry.has(type, SUBTYPE_BASE)) {
145
+ return SUBTYPE_BASE;
146
+ }
147
+ return candidate;
148
+ }
149
+
150
+ function splitTypeKey(key: string, registry: TypeRegistry): SplitKey {
151
+ const dotIdx = key.indexOf(TYPE_SUBTYPE_SEPARATOR);
152
+ if (dotIdx < 0) {
153
+ // Bare type, no fused subType — resolve via the registry default.
154
+ return { type: key, subType: defaultSubTypeFor(key, registry), explicit: false };
155
+ }
156
+ const type = key.slice(0, dotIdx);
157
+ const subType = key.slice(dotIdx + TYPE_SUBTYPE_SEPARATOR.length);
158
+ // Malformed keys (e.g. "object.entity.extra", ".entity", "object.") are not
159
+ // validated here — they fall through to the downstream registry.has() check
160
+ // which reports them as an unknown type or subtype error.
161
+ return { type, subType, explicit: true };
162
+ }
163
+
164
+ // ---------------------------------------------------------------------------
165
+ // Helper: expand relative package paths
166
+ // ---------------------------------------------------------------------------
167
+
168
+ /**
169
+ * Expands a package path relative to a base package.
170
+ * Java semantics:
171
+ * - Absolute path (::foo::bar) → prepended with base: "acme" + "::foo" → "acme::foo::bar"
172
+ * - Relative parent (..) → handled in super resolution, not here
173
+ * - No leading :: → used as-is
174
+ */
175
+ function expandPackageForPath(basePkg: string, pkgPath: string): string {
176
+ if (basePkg.trim() === "" || !pkgPath.startsWith(PACKAGE_SEPARATOR)) {
177
+ return pkgPath;
178
+ }
179
+ return basePkg + pkgPath;
180
+ }
181
+
182
+ // ---------------------------------------------------------------------------
183
+ // Main entry point
184
+ // ---------------------------------------------------------------------------
185
+
186
+ // Module-level flag consumed by parseNodeFresh during super resolution.
187
+ // Safe because buildTree is fully synchronous — no reentrancy risk within
188
+ // a single parse call. Set at buildTree entry, read deep in the call tree.
189
+ let _deferSuperResolution = false;
190
+
191
+ /**
192
+ * buildTree — the shared registry-driven tree-builder.
193
+ *
194
+ * Turns an already-parsed *canonical-shaped* JS object (from JSON.parse, or
195
+ * from the YAML parser's desugar) into a typed MetaData tree. Both parseJson
196
+ * and parseYaml funnel through here, guaranteeing one structure / two
197
+ * renderings stay isomorphic.
198
+ *
199
+ * Throws ParseError for top-level structural problems (matching the historical
200
+ * parseJson behavior); collects content-level problems into the result.
201
+ */
202
+ export function buildTree(parsed: unknown, opts: ParseOptions): ParseResult {
203
+ const warnings: string[] = [];
204
+ const errors: ParseError[] = [];
205
+ const strict = opts.strict ?? false;
206
+ const source = opts.sourceName;
207
+ _deferSuperResolution = opts.deferSuperResolution === true;
208
+
209
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
210
+ throw new ParseError("Top-level metadata must be an object", { ...errOpts(source), code: "ERR_TOP_LEVEL_NOT_OBJECT" });
211
+ }
212
+
213
+ const topLevel = parsed as Record<string, unknown>;
214
+
215
+ // --- Find the wrapper key (skip $schema) ---
216
+ const wrapperKeys = Object.keys(topLevel).filter((k) => k !== JSON_KEY_SCHEMA);
217
+
218
+ if (wrapperKeys.length === 0) {
219
+ throw new ParseError("Top-level metadata object has no type wrapper key", { ...errOpts(source), code: "ERR_TOP_LEVEL_NOT_OBJECT" });
220
+ }
221
+ if (wrapperKeys.length > 1) {
222
+ throw new ParseError(
223
+ `Top-level metadata object must have exactly one wrapper key (found: ${wrapperKeys.join(", ")})`,
224
+ { ...errOpts(source), code: "ERR_TOP_LEVEL_NOT_OBJECT" },
225
+ );
226
+ }
227
+
228
+ const rootKey = wrapperKeys[0]!;
229
+ const rootData = topLevel[rootKey];
230
+
231
+ if (typeof rootData !== "object" || rootData === null || Array.isArray(rootData)) {
232
+ throw new ParseError(
233
+ `Top-level wrapper "${rootKey}" must contain an object`,
234
+ { ...errOpts(source, rootKey), code: "ERR_TOP_LEVEL_NOT_OBJECT" },
235
+ );
236
+ }
237
+
238
+ const rootDataObj = rootData as Record<string, unknown>;
239
+ const { type: rootType, subType: rootSubType } = splitTypeKey(rootKey, opts.registry);
240
+
241
+ // Check root type is registered (always throw — can't skip the root)
242
+ if (!opts.registry.has(rootType, rootSubType)) {
243
+ const rootTypeCode = opts.registry.allSubTypesOf(rootType).length > 0
244
+ ? "ERR_UNKNOWN_SUBTYPE" as const
245
+ : "ERR_UNKNOWN_TYPE" as const;
246
+ throw new ParseError(
247
+ `Unknown root type "${rootType}.${rootSubType}" — not registered`,
248
+ { ...errOpts(source, rootKey), code: rootTypeCode },
249
+ );
250
+ }
251
+
252
+ if (opts.intoRoot !== undefined) {
253
+ // --- Merge mode: parse root's attrs/children into the existing root ---
254
+ // The JSON root's own package/super/reserved-keys are not re-applied to the
255
+ // existing root. BUT: children from the NEW JSON should inherit from the
256
+ // NEW root's package, not the existing root's.
257
+ const newRootPkg = rootDataObj[RESERVED_KEY_PACKAGE];
258
+ const contextPkg = (typeof newRootPkg === "string" ? newRootPkg : opts.intoRoot.package) ?? "";
259
+ parseNodeInto(
260
+ rootDataObj,
261
+ opts.intoRoot,
262
+ opts.intoRoot, // accumulating root for super resolution
263
+ contextPkg,
264
+ opts.registry,
265
+ warnings,
266
+ errors,
267
+ strict,
268
+ source,
269
+ rootKey,
270
+ );
271
+ return { root: opts.intoRoot, warnings, errors };
272
+ }
273
+
274
+ // --- Fresh root mode: create a new root from the JSON ---
275
+ // The cast is safe within the core provider: `metadata.root` is the only
276
+ // registered `metadata` subtype, and its factory unconditionally produces a
277
+ // MetaRoot (see core-types.ts). A registry that registered a second
278
+ // `metadata.*` subtype backed by a non-MetaRoot factory would break this
279
+ // cast — a known limitation of the `TypeDefinition.factory: => MetaData`
280
+ // signature. `parseNodeFresh` is a general node parser, so MetaData is its
281
+ // correct return type; this top-level callsite is where the doc-root invariant holds.
282
+ const root = parseNodeFresh(
283
+ rootType,
284
+ rootSubType,
285
+ rootDataObj,
286
+ undefined, // no accumulating root yet — built as we go
287
+ "", // no inherited context pkg yet for the root itself
288
+ opts.registry,
289
+ warnings,
290
+ errors,
291
+ strict,
292
+ source,
293
+ rootKey,
294
+ ) as MetaRoot;
295
+ return { root, warnings, errors };
296
+ }
297
+
298
+ // ---------------------------------------------------------------------------
299
+ // parseNodeFresh — creates a NEW node for this metadata node.
300
+ //
301
+ // `accumRoot` is the root of the accumulating tree (for super resolution).
302
+ // When parsing the root node itself, accumRoot is undefined on first call;
303
+ // the newly created root is passed as accumRoot for its children.
304
+ //
305
+ // `inheritedContextPkg` is the effective package from the nearest ancestor
306
+ // that had an explicit package. Used when this node has no own package.
307
+ // ---------------------------------------------------------------------------
308
+
309
+ function parseNodeFresh(
310
+ type: string,
311
+ subType: string,
312
+ nodeData: Record<string, unknown>,
313
+ accumRoot: MetaData | undefined,
314
+ inheritedContextPkg: string,
315
+ registry: TypeRegistry,
316
+ warnings: string[],
317
+ errors: ParseError[],
318
+ strict: boolean,
319
+ source: string | undefined,
320
+ path: string,
321
+ parentType?: string, // optional: type of the parent node (for inheritance rules)
322
+ parent?: MetaData, // optional: the parent model itself (for checking fqn)
323
+ ): MetaData {
324
+ // --- Look up type in registry ---
325
+ if (!registry.has(type, subType)) {
326
+ if (registry.has(type, SUBTYPE_BASE)) {
327
+ subType = SUBTYPE_BASE;
328
+ } else {
329
+ const msg = `Unknown type "${type}.${subType}" — not registered`;
330
+ errors.push(new ParseError(msg, { ...errOpts(source, path), code: "ERR_UNKNOWN_TYPE" }));
331
+ const rawName = nodeData[RESERVED_KEY_NAME];
332
+ const name = typeof rawName === "string" ? rawName : "";
333
+ return new MetaRoot(new TypeId(type, subType), name);
334
+ }
335
+ }
336
+
337
+ // --- Determine name ---
338
+ const rawName = nodeData[RESERVED_KEY_NAME];
339
+ const name = typeof rawName === "string" ? rawName : "";
340
+
341
+ // --- Create the model ---
342
+ const def = registry.find(type, subType)!;
343
+ const model = def.factory(def.typeId, name);
344
+
345
+ // --- Apply reserved keys (package, extends, abstract, isArray) ---
346
+ applyReservedKeys(model, nodeData, strict, source, path, warnings, inheritedContextPkg);
347
+
348
+ // --- Inherit package from context if not explicitly set ---
349
+ // Java rule (BaseMetaDataParser.shouldInheritPackageFromParent):
350
+ // - Fields within objects: no package (simple names)
351
+ // - Fields NOT within objects: inherit parent's package (for abstract fields at root level)
352
+ // - Validators within fields: inherit the field's package ONLY if field's FQN contains "::"
353
+ // - Objects and other types: don't inherit
354
+ if (model.package === undefined && inheritedContextPkg !== "") {
355
+ let shouldInherit = false;
356
+
357
+ if (type === TYPE_FIELD && parentType !== TYPE_OBJECT) {
358
+ shouldInherit = true;
359
+ } else if (type === TYPE_VALIDATOR && parentType === TYPE_FIELD && parent !== undefined) {
360
+ shouldInherit = parent.fqn().includes(PACKAGE_SEPARATOR);
361
+ }
362
+
363
+ if (shouldInherit) {
364
+ model.setPackage(inheritedContextPkg);
365
+ }
366
+ }
367
+
368
+ // --- Determine the effective context package for super resolution ---
369
+ const effectivePkg = model.package ?? inheritedContextPkg;
370
+
371
+ // --- Resolve super IMMEDIATELY against the accumulating root ---
372
+ // (Skipped when deferSuperResolution is true — the loader resolves after
373
+ // all input files have been parsed, so cross-file super refs work.)
374
+ if (model.superRef !== undefined && accumRoot !== undefined && !_deferSuperResolution) {
375
+ const superModel = resolveSuperRef(model.superRef, effectivePkg, accumRoot);
376
+ if (superModel !== undefined) {
377
+ model.setSuperResolved(superModel);
378
+ } else {
379
+ throw new ParseError(
380
+ `the SuperClass '${model.superRef}' does not exist in file '${source ?? "<unknown>"}'`,
381
+ { ...errOpts(source, path), code: "ERR_UNRESOLVED_SUPER" },
382
+ );
383
+ }
384
+ } else if (model.superRef !== undefined && accumRoot === undefined) {
385
+ // Root node has a super ref — not resolvable against itself.
386
+ reportProblem(
387
+ `super on root node ('${model.superRef}') is not supported and will be ignored`,
388
+ strict,
389
+ warnings,
390
+ source,
391
+ path,
392
+ "ERR_UNRESOLVED_SUPER",
393
+ );
394
+ }
395
+
396
+ // --- Process inline attributes and other keys ---
397
+ applyInlineAttrsAndUnknownKeys(model, nodeData, strict, source, path, warnings, registry);
398
+
399
+ // --- Process children ---
400
+ // For the root node we use itself as the accumRoot for its children.
401
+ const childAccumRoot = accumRoot ?? model;
402
+ const childInheritedContextPkg = model.package ?? inheritedContextPkg;
403
+ processChildren(model, nodeData, childAccumRoot, childInheritedContextPkg, registry, warnings, errors, strict, source, path);
404
+
405
+ return model;
406
+ }
407
+
408
+ // ---------------------------------------------------------------------------
409
+ // parseNodeInto — merges a JSON node's attrs/children into an EXISTING model.
410
+ //
411
+ // Used for the intoRoot mode (top-level merge), overlay: true nodes, and the
412
+ // default same-name reuse path. Does NOT re-apply reserved keys (subType,
413
+ // extends, package) — those belong to the original model's identity.
414
+ // ---------------------------------------------------------------------------
415
+
416
+ function parseNodeInto(
417
+ nodeData: Record<string, unknown>,
418
+ target: MetaData,
419
+ accumRoot: MetaData,
420
+ inheritedContextPkg: string,
421
+ registry: TypeRegistry,
422
+ warnings: string[],
423
+ errors: ParseError[],
424
+ strict: boolean,
425
+ source: string | undefined,
426
+ path: string,
427
+ ): void {
428
+ // Apply inline attrs (not reserved keys — those stay on the existing model)
429
+ applyInlineAttrsAndUnknownKeys(target, nodeData, strict, source, path, warnings, registry);
430
+
431
+ // The effective package for children: use inheritedContextPkg (from the new
432
+ // JSON's root) — not target.package, because target is the existing model
433
+ // being merged into, and new children inherit from the NEW context.
434
+ const effectivePkg = inheritedContextPkg;
435
+
436
+ processChildren(target, nodeData, accumRoot, effectivePkg, registry, warnings, errors, strict, source, path);
437
+ }
438
+
439
+ // ---------------------------------------------------------------------------
440
+ // createOrFindMetaData — per-node merge logic.
441
+ //
442
+ // overlay: true → find-or-throw (throw if no existing same-(type,name) child)
443
+ // default → find-or-create (silently reuse if found)
444
+ // ---------------------------------------------------------------------------
445
+
446
+ function createOrFindMetaData(
447
+ type: string,
448
+ subType: string,
449
+ nodeData: Record<string, unknown>,
450
+ parent: MetaData,
451
+ accumRoot: MetaData,
452
+ inheritedContextPkg: string,
453
+ registry: TypeRegistry,
454
+ warnings: string[],
455
+ errors: ParseError[],
456
+ strict: boolean,
457
+ source: string | undefined,
458
+ path: string,
459
+ ): MetaData | undefined {
460
+ // Only `overlay: true` re-opens an existing node. Anything else falls through
461
+ // to the default reuse-or-create path.
462
+ const isOverlayNode = nodeData[RESERVED_KEY_OVERLAY] === true;
463
+
464
+ // Determine name (needed for the lookup). Lookup key is (type, name).
465
+ const rawName = nodeData[RESERVED_KEY_NAME];
466
+ const name = typeof rawName === "string" ? rawName : "";
467
+
468
+ // Look up an existing child with (type, name). Skip unnamed nodes — they
469
+ // are always distinct (e.g. inline validators, anonymous attrs).
470
+ const existing = name !== "" ? parent.ownChildByTypeAndName(type, name) : undefined;
471
+
472
+ if (isOverlayNode) {
473
+ if (existing === undefined) {
474
+ throw new ParseError(
475
+ `Overlay operation requested for [${type}:${name}] but no existing metadata found to merge into`,
476
+ { ...errOpts(source, path), code: "ERR_OVERLAY_NO_TARGET" },
477
+ );
478
+ }
479
+ existing.setIsMerge(true);
480
+ parseNodeInto(nodeData, existing, accumRoot, inheritedContextPkg, registry, warnings, errors, strict, source, path);
481
+ return existing;
482
+ }
483
+
484
+ // Default: no operator → silently reuse existing or create new.
485
+ if (existing !== undefined) {
486
+ parseNodeInto(nodeData, existing, accumRoot, inheritedContextPkg, registry, warnings, errors, strict, source, path);
487
+ return existing;
488
+ }
489
+
490
+ // Not found (or unnamed) → create new
491
+ return parseNodeFresh(type, subType, nodeData, accumRoot, inheritedContextPkg, registry, warnings, errors, strict, source, path, parent.type, parent);
492
+ }
493
+
494
+ // ---------------------------------------------------------------------------
495
+ // applyReservedKeys — apply package, extends, abstract, isArray to a model.
496
+ //
497
+ // Called only when CREATING a new model (not when merging into existing).
498
+ // ---------------------------------------------------------------------------
499
+
500
+ function applyReservedKeys(
501
+ model: MetaData,
502
+ nodeData: Record<string, unknown>,
503
+ strict: boolean,
504
+ source: string | undefined,
505
+ path: string,
506
+ warnings: string[],
507
+ contextPkg?: string,
508
+ ): void {
509
+ // package
510
+ const rawPkg = nodeData[RESERVED_KEY_PACKAGE];
511
+ if (rawPkg !== undefined) {
512
+ if (typeof rawPkg !== "string") {
513
+ reportProblem(`"${RESERVED_KEY_PACKAGE}" must be a string at ${path}`, strict, warnings, source, path, "ERR_BAD_ATTR_VALUE");
514
+ } else {
515
+ const expandedPkg = contextPkg !== undefined ? expandPackageForPath(contextPkg, rawPkg) : rawPkg;
516
+ model.setPackage(expandedPkg);
517
+ }
518
+ }
519
+
520
+ // extends — store the raw supertype ref; resolution happens after this call.
521
+ const rawExtends = nodeData[RESERVED_KEY_EXTENDS];
522
+ if (rawExtends !== undefined) {
523
+ if (typeof rawExtends !== "string") {
524
+ reportProblem(`"${RESERVED_KEY_EXTENDS}" must be a string at ${path}`, strict, warnings, source, path, "ERR_UNRESOLVED_SUPER");
525
+ } else {
526
+ model.setSuper(rawExtends);
527
+ }
528
+ }
529
+
530
+ // abstract — structural key (true → abstract node)
531
+ const rawAbstract = nodeData[RESERVED_KEY_ABSTRACT];
532
+ if (rawAbstract !== undefined) {
533
+ if (typeof rawAbstract !== "boolean") {
534
+ reportProblem(`"${RESERVED_KEY_ABSTRACT}" must be a boolean at ${path}`, strict, warnings, source, path, "ERR_BAD_ATTR_VALUE");
535
+ } else {
536
+ model.setIsAbstract(rawAbstract);
537
+ }
538
+ }
539
+
540
+ // isArray — structural key (true → array node)
541
+ const rawIsArray = nodeData[RESERVED_KEY_IS_ARRAY];
542
+ if (rawIsArray !== undefined) {
543
+ if (typeof rawIsArray !== "boolean") {
544
+ reportProblem(`"${RESERVED_KEY_IS_ARRAY}" must be a boolean at ${path}`, strict, warnings, source, path, "ERR_BAD_ATTR_VALUE");
545
+ } else {
546
+ model.setIsArray(rawIsArray);
547
+ }
548
+ }
549
+ }
550
+
551
+ // ---------------------------------------------------------------------------
552
+ // applyInlineAttrsAndUnknownKeys — apply @-prefixed attrs and warn about unknowns
553
+ //
554
+ // Called for both fresh creates AND merge-into-existing paths.
555
+ // Does NOT process reserved structural keys.
556
+ // ---------------------------------------------------------------------------
557
+
558
+ function applyInlineAttrsAndUnknownKeys(
559
+ model: MetaData,
560
+ nodeData: Record<string, unknown>,
561
+ strict: boolean,
562
+ source: string | undefined,
563
+ path: string,
564
+ warnings: string[],
565
+ registry: TypeRegistry,
566
+ ): void {
567
+ for (const key of Object.keys(nodeData)) {
568
+ // Skip all reserved structural keys (already handled or intentionally ignored)
569
+ if (RESERVED_KEYS.has(key)) continue;
570
+
571
+ if (!key.startsWith(ATTR_PREFIX)) {
572
+ const displayName =
573
+ model.name !== "" ? `${model.type}.${model.subType} '${model.name}'` : `${model.type}.${model.subType}`;
574
+ reportProblem(
575
+ `Unknown key '${key}' on ${displayName} at ${path} (must be reserved or ${ATTR_PREFIX}-prefixed)`,
576
+ strict, warnings, source, path, "ERR_UNKNOWN_ATTR",
577
+ );
578
+ continue;
579
+ }
580
+
581
+ // Inline attribute (@-prefixed) — materialize into a MetaAttr instance.
582
+ const attrName = key.slice(ATTR_PREFIX.length);
583
+ const rawVal = nodeData[key];
584
+
585
+ try {
586
+ const attr = materializeAttr(model, attrName, rawVal, registry);
587
+ model.setMetaAttr(attr);
588
+ } catch (err) {
589
+ reportProblem(
590
+ `Failed to convert attribute "${ATTR_PREFIX}${attrName}" at ${path}: ${(err as Error).message}`,
591
+ strict, warnings, source, path, "ERR_BAD_ATTR_VALUE",
592
+ );
593
+ }
594
+ }
595
+ }
596
+
597
+ // ---------------------------------------------------------------------------
598
+ // materializeAttr — build a single attr into the right MetaAttr subclass:
599
+ // declared subtype from the owner's AttrSchema (if any), else inferred from the
600
+ // value shape. The instance coerces + desugars its own value.
601
+ // ---------------------------------------------------------------------------
602
+
603
+ function materializeAttr(
604
+ owner: MetaData,
605
+ attrName: string,
606
+ rawVal: unknown,
607
+ registry: TypeRegistry,
608
+ ): MetaAttr {
609
+ const attrSpec = registry.attrsOf(owner.type, owner.subType).find((s) => s.name === attrName);
610
+ let subType: string;
611
+ if (attrSpec !== undefined && attrSpec.valueType !== undefined) {
612
+ subType = attrSpec.valueType;
613
+ } else {
614
+ // Undeclared or declared-but-untyped (@default): preserve the author's shape.
615
+ subType = inferUndeclaredAttrSubType(rawVal);
616
+ }
617
+ const def = registry.find(TYPE_ATTR, subType);
618
+ const node = (def !== undefined
619
+ ? def.factory(def.typeId, attrName)
620
+ : new MetaAttr(new TypeId(TYPE_ATTR, subType), attrName)) as MetaAttr;
621
+ const coerced = node.coerce(rawVal);
622
+ const desugared = node.desugar(coerced);
623
+ node.setAttr(RESERVED_KEY_VALUE, desugared);
624
+ return node;
625
+ }
626
+
627
+ // Undeclared attr → pick the subtype from the value's runtime shape, preserving
628
+ // type (a numeric string stays string). Wraps inferAttrSubType (scalar/array
629
+ // rule, incl. the int/long/double range split) with the object + null-reject
630
+ // branches that predate object attrs: a plain object → properties; null /
631
+ // undefined are not valid undeclared attr values.
632
+ function inferUndeclaredAttrSubType(raw: unknown): string {
633
+ if (raw === null || raw === undefined) {
634
+ throw new Error(`${raw === null ? "null" : "undefined"} is not a valid attr value`);
635
+ }
636
+ if (typeof raw === "object" && !Array.isArray(raw)) return ATTR_SUBTYPE_PROPERTIES;
637
+ return inferAttrSubType(raw as AttrValue);
638
+ }
639
+
640
+ // ---------------------------------------------------------------------------
641
+ // processChildren — parse the "children" array of a node
642
+ // ---------------------------------------------------------------------------
643
+
644
+ function processChildren(
645
+ parent: MetaData,
646
+ nodeData: Record<string, unknown>,
647
+ accumRoot: MetaData,
648
+ inheritedContextPkg: string,
649
+ registry: TypeRegistry,
650
+ warnings: string[],
651
+ errors: ParseError[],
652
+ strict: boolean,
653
+ source: string | undefined,
654
+ path: string,
655
+ ): void {
656
+ const rawChildren = nodeData[RESERVED_KEY_CHILDREN];
657
+ if (rawChildren === undefined) return;
658
+
659
+ if (!Array.isArray(rawChildren)) {
660
+ reportProblem(`"${RESERVED_KEY_CHILDREN}" must be an array at ${path}`, strict, warnings, source, path, "ERR_TOP_LEVEL_NOT_OBJECT");
661
+ return;
662
+ }
663
+
664
+ for (let i = 0; i < rawChildren.length; i++) {
665
+ const childEntry = rawChildren[i];
666
+ const childPath = `${path}.${RESERVED_KEY_CHILDREN}[${i}]`;
667
+
668
+ if (typeof childEntry !== "object" || childEntry === null || Array.isArray(childEntry)) {
669
+ reportProblem(`Child at ${childPath} must be an object`, strict, warnings, source, childPath, "ERR_TOP_LEVEL_NOT_OBJECT");
670
+ continue;
671
+ }
672
+
673
+ const childRecord = childEntry as Record<string, unknown>;
674
+ const childKeys = Object.keys(childRecord);
675
+
676
+ if (childKeys.length !== 1) {
677
+ const msg =
678
+ childKeys.length === 0
679
+ ? `Child at ${childPath} has no type wrapper key`
680
+ : `Child at ${childPath} has multiple keys (${childKeys.join(", ")}) — each child must have exactly one wrapper key`;
681
+ reportProblem(msg, strict, warnings, source, childPath, "ERR_TOP_LEVEL_NOT_OBJECT");
682
+ continue;
683
+ }
684
+
685
+ const childKey = childKeys[0]!;
686
+ const childData = childRecord[childKey];
687
+ const childNodePath = `${childPath}.${childKey}`;
688
+
689
+ if (typeof childData !== "object" || childData === null || Array.isArray(childData)) {
690
+ reportProblem(
691
+ `Child wrapper "${childKey}" at ${childNodePath} must contain an object`,
692
+ strict, warnings, source, childNodePath, "ERR_TOP_LEVEL_NOT_OBJECT",
693
+ );
694
+ continue;
695
+ }
696
+
697
+ const childDataObj = childData as Record<string, unknown>;
698
+ const { type: childType, subType: childSubTypeRaw, explicit } = splitTypeKey(childKey, registry);
699
+
700
+ // --- Check if this child type is registered ---
701
+ // An EXPLICIT unknown subType (fused into the key) is an error — never
702
+ // silently downgraded to base. An OMITTED subType that resolves to an
703
+ // unregistered default falls back to base.
704
+ let childSubType = childSubTypeRaw;
705
+ if (!registry.has(childType, childSubType)) {
706
+ if (!explicit && registry.has(childType, SUBTYPE_BASE)) {
707
+ childSubType = SUBTYPE_BASE;
708
+ } else {
709
+ const childTypeCode = explicit && registry.allSubTypesOf(childType).length > 0
710
+ ? "ERR_UNKNOWN_SUBTYPE" as const
711
+ : "ERR_UNKNOWN_TYPE" as const;
712
+ errors.push(
713
+ new ParseError(
714
+ `Unknown type "${childType}.${childSubType}" — not registered`,
715
+ { ...errOpts(source, childNodePath), code: childTypeCode },
716
+ ),
717
+ );
718
+ continue; // skip this child
719
+ }
720
+ }
721
+
722
+ // --- Special handling for "attr" child nodes ---
723
+ if (childType === TYPE_ATTR) {
724
+ parseAttrChild(parent, childType, childSubType, childDataObj, registry, warnings, strict, source, childNodePath);
725
+ } else {
726
+ // Use createOrFindMetaData to handle overlay/default per-node logic
727
+ const childModel = createOrFindMetaData(
728
+ childType,
729
+ childSubType,
730
+ childDataObj,
731
+ parent,
732
+ accumRoot,
733
+ inheritedContextPkg,
734
+ registry,
735
+ warnings,
736
+ errors,
737
+ strict,
738
+ source,
739
+ childNodePath,
740
+ );
741
+
742
+ if (childModel !== undefined && !parent.ownChildren().includes(childModel)) {
743
+ parent.addChild(childModel);
744
+ }
745
+ }
746
+ }
747
+ }
748
+
749
+ // ---------------------------------------------------------------------------
750
+ // Attr child node — materialize into a MetaAttr instance (NOT a child).
751
+ // ---------------------------------------------------------------------------
752
+ //
753
+ // A typed attr is encoded as { "attr.<subType>": { "name": ..., "value": ... } }
754
+ // inside a node's children array. The subType is fused into the wrapper key.
755
+ //
756
+ // The instance coerces + desugars toward its OWN subtype (the wrapper key's
757
+ // subType, e.g. attr.stringarray) — StringArrayAttr.coerce wraps the bare
758
+ // string, FilterAttr.desugar normalizes. The attr is attached via setMetaAttr,
759
+ // never addChild: attrs are no longer structural children (D2/D5).
760
+
761
+ function parseAttrChild(
762
+ parent: MetaData,
763
+ attrType: string,
764
+ attrSubType: string,
765
+ attrData: Record<string, unknown>,
766
+ registry: TypeRegistry,
767
+ warnings: string[],
768
+ strict: boolean,
769
+ source: string | undefined,
770
+ path: string,
771
+ ): void {
772
+ const attrName = attrData[RESERVED_KEY_NAME];
773
+ const attrValue = attrData[RESERVED_KEY_VALUE];
774
+
775
+ if (typeof attrName !== "string" || attrName === "") {
776
+ reportProblem(
777
+ `attr child at ${path} requires a non-empty "${RESERVED_KEY_NAME}" string`,
778
+ strict, warnings, source, path, "ERR_MISSING_REQUIRED_ATTR",
779
+ );
780
+ return;
781
+ }
782
+
783
+ if (attrValue === undefined) {
784
+ reportProblem(
785
+ `attr child "${attrName}" at ${path} is missing "${RESERVED_KEY_VALUE}"`,
786
+ strict, warnings, source, path, "ERR_MISSING_REQUIRED_ATTR",
787
+ );
788
+ return;
789
+ }
790
+
791
+ // Resolve the attr node's own subtype (fall back to base if unregistered).
792
+ const resolvedSubType =
793
+ registry.has(attrType, attrSubType) || !registry.has(attrType, SUBTYPE_BASE)
794
+ ? attrSubType
795
+ : SUBTYPE_BASE;
796
+ const attrDef = registry.find(attrType, resolvedSubType);
797
+
798
+ const node = (attrDef !== undefined
799
+ ? attrDef.factory(attrDef.typeId, attrName)
800
+ : new MetaAttr(new TypeId(attrType, resolvedSubType), attrName)) as MetaAttr;
801
+
802
+ try {
803
+ const coerced = node.coerce(attrValue);
804
+ const desugared = node.desugar(coerced);
805
+ node.setAttr(RESERVED_KEY_VALUE, desugared);
806
+ } catch (err) {
807
+ reportProblem(
808
+ `Failed to convert attr child "${attrName}" value at ${path}: ${(err as Error).message}`,
809
+ strict, warnings, source, path, "ERR_BAD_ATTR_VALUE",
810
+ );
811
+ return;
812
+ }
813
+
814
+ parent.setMetaAttr(node);
815
+ }