@metaobjectsdev/codegen-ts 0.9.0 → 0.11.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 (342) hide show
  1. package/README.md +1 -1
  2. package/dist/column-mapper.d.ts +12 -6
  3. package/dist/column-mapper.d.ts.map +1 -1
  4. package/dist/column-mapper.js +68 -28
  5. package/dist/column-mapper.js.map +1 -1
  6. package/dist/constants.d.ts +8 -0
  7. package/dist/constants.d.ts.map +1 -1
  8. package/dist/constants.js +16 -0
  9. package/dist/constants.js.map +1 -1
  10. package/dist/docs-paths.d.ts +58 -0
  11. package/dist/docs-paths.d.ts.map +1 -0
  12. package/dist/docs-paths.js +89 -0
  13. package/dist/docs-paths.js.map +1 -0
  14. package/dist/enum-import.d.ts +14 -0
  15. package/dist/enum-import.d.ts.map +1 -0
  16. package/dist/enum-import.js +35 -0
  17. package/dist/enum-import.js.map +1 -0
  18. package/dist/enum-shared.d.ts +32 -0
  19. package/dist/enum-shared.d.ts.map +1 -0
  20. package/dist/enum-shared.js +83 -0
  21. package/dist/enum-shared.js.map +1 -0
  22. package/dist/generator-registry.d.ts +22 -0
  23. package/dist/generator-registry.d.ts.map +1 -0
  24. package/dist/generator-registry.js +161 -0
  25. package/dist/generator-registry.js.map +1 -0
  26. package/dist/generator.d.ts +6 -0
  27. package/dist/generator.d.ts.map +1 -1
  28. package/dist/generator.js.map +1 -1
  29. package/dist/generators/api-doc-render.d.ts +17 -0
  30. package/dist/generators/api-doc-render.d.ts.map +1 -0
  31. package/dist/generators/api-doc-render.js +431 -0
  32. package/dist/generators/api-doc-render.js.map +1 -0
  33. package/dist/generators/api-docs-file.d.ts +21 -0
  34. package/dist/generators/api-docs-file.d.ts.map +1 -0
  35. package/dist/generators/api-docs-file.js +112 -0
  36. package/dist/generators/api-docs-file.js.map +1 -0
  37. package/dist/generators/api-field-shape.d.ts +39 -0
  38. package/dist/generators/api-field-shape.d.ts.map +1 -0
  39. package/dist/generators/api-field-shape.js +92 -0
  40. package/dist/generators/api-field-shape.js.map +1 -0
  41. package/dist/generators/api-label.d.ts +3 -0
  42. package/dist/generators/api-label.d.ts.map +1 -0
  43. package/dist/generators/api-label.js +8 -0
  44. package/dist/generators/api-label.js.map +1 -0
  45. package/dist/generators/api-model.d.ts +122 -0
  46. package/dist/generators/api-model.d.ts.map +1 -0
  47. package/dist/generators/api-model.js +809 -0
  48. package/dist/generators/api-model.js.map +1 -0
  49. package/dist/generators/docs-data-builder.d.ts +26 -4
  50. package/dist/generators/docs-data-builder.d.ts.map +1 -1
  51. package/dist/generators/docs-data-builder.js +439 -167
  52. package/dist/generators/docs-data-builder.js.map +1 -1
  53. package/dist/generators/docs-data.d.ts +136 -27
  54. package/dist/generators/docs-data.d.ts.map +1 -1
  55. package/dist/generators/docs-data.js +1 -1
  56. package/dist/generators/docs-data.js.map +1 -1
  57. package/dist/generators/docs-file.d.ts +19 -0
  58. package/dist/generators/docs-file.d.ts.map +1 -1
  59. package/dist/generators/docs-file.js +154 -27
  60. package/dist/generators/docs-file.js.map +1 -1
  61. package/dist/generators/entity-file.d.ts.map +1 -1
  62. package/dist/generators/entity-file.js +29 -14
  63. package/dist/generators/entity-file.js.map +1 -1
  64. package/dist/generators/extractor-file.d.ts.map +1 -1
  65. package/dist/generators/extractor-file.js +2 -1
  66. package/dist/generators/extractor-file.js.map +1 -1
  67. package/dist/generators/field-anchor.d.ts +7 -0
  68. package/dist/generators/field-anchor.d.ts.map +1 -0
  69. package/dist/generators/field-anchor.js +23 -0
  70. package/dist/generators/field-anchor.js.map +1 -0
  71. package/dist/generators/index.d.ts +8 -1
  72. package/dist/generators/index.d.ts.map +1 -1
  73. package/dist/generators/index.js +6 -0
  74. package/dist/generators/index.js.map +1 -1
  75. package/dist/generators/mermaid-er.d.ts +14 -0
  76. package/dist/generators/mermaid-er.d.ts.map +1 -1
  77. package/dist/generators/mermaid-er.js +14 -0
  78. package/dist/generators/mermaid-er.js.map +1 -1
  79. package/dist/generators/output-parser-file.d.ts.map +1 -1
  80. package/dist/generators/output-parser-file.js +3 -4
  81. package/dist/generators/output-parser-file.js.map +1 -1
  82. package/dist/generators/output-prompt-file.d.ts.map +1 -1
  83. package/dist/generators/output-prompt-file.js +2 -2
  84. package/dist/generators/output-prompt-file.js.map +1 -1
  85. package/dist/generators/prompt-render-file.d.ts.map +1 -1
  86. package/dist/generators/prompt-render-file.js +3 -4
  87. package/dist/generators/prompt-render-file.js.map +1 -1
  88. package/dist/generators/queries-file.d.ts.map +1 -1
  89. package/dist/generators/queries-file.js +8 -3
  90. package/dist/generators/queries-file.js.map +1 -1
  91. package/dist/generators/render-helper-file.d.ts.map +1 -1
  92. package/dist/generators/render-helper-file.js +2 -2
  93. package/dist/generators/render-helper-file.js.map +1 -1
  94. package/dist/generators/routes-file-hono.d.ts.map +1 -1
  95. package/dist/generators/routes-file-hono.js +5 -1
  96. package/dist/generators/routes-file-hono.js.map +1 -1
  97. package/dist/generators/routes-file.d.ts +3 -0
  98. package/dist/generators/routes-file.d.ts.map +1 -1
  99. package/dist/generators/routes-file.js +6 -1
  100. package/dist/generators/routes-file.js.map +1 -1
  101. package/dist/generators/template-doc-builder.d.ts +19 -0
  102. package/dist/generators/template-doc-builder.d.ts.map +1 -0
  103. package/dist/generators/template-doc-builder.js +220 -0
  104. package/dist/generators/template-doc-builder.js.map +1 -0
  105. package/dist/generators/template-doc-data.d.ts +62 -0
  106. package/dist/generators/template-doc-data.d.ts.map +1 -0
  107. package/dist/generators/template-doc-data.js +16 -0
  108. package/dist/generators/template-doc-data.js.map +1 -0
  109. package/dist/generators/template-payload-tree.d.ts +15 -0
  110. package/dist/generators/template-payload-tree.d.ts.map +1 -0
  111. package/dist/generators/template-payload-tree.js +61 -0
  112. package/dist/generators/template-payload-tree.js.map +1 -0
  113. package/dist/generators/template-source-annotate.d.ts +74 -0
  114. package/dist/generators/template-source-annotate.d.ts.map +1 -0
  115. package/dist/generators/template-source-annotate.js +184 -0
  116. package/dist/generators/template-source-annotate.js.map +1 -0
  117. package/dist/generators/template-source-render.d.ts +24 -0
  118. package/dist/generators/template-source-render.d.ts.map +1 -0
  119. package/dist/generators/template-source-render.js +175 -0
  120. package/dist/generators/template-source-render.js.map +1 -0
  121. package/dist/generators/trace-helper-file.d.ts +9 -0
  122. package/dist/generators/trace-helper-file.d.ts.map +1 -0
  123. package/dist/generators/trace-helper-file.js +196 -0
  124. package/dist/generators/trace-helper-file.js.map +1 -0
  125. package/dist/import-path.d.ts +18 -0
  126. package/dist/import-path.d.ts.map +1 -1
  127. package/dist/import-path.js +21 -0
  128. package/dist/import-path.js.map +1 -1
  129. package/dist/index.d.ts +29 -4
  130. package/dist/index.d.ts.map +1 -1
  131. package/dist/index.js +28 -2
  132. package/dist/index.js.map +1 -1
  133. package/dist/metaobjects-config.d.ts +101 -2
  134. package/dist/metaobjects-config.d.ts.map +1 -1
  135. package/dist/metaobjects-config.js +46 -0
  136. package/dist/metaobjects-config.js.map +1 -1
  137. package/dist/naming.d.ts +39 -2
  138. package/dist/naming.d.ts.map +1 -1
  139. package/dist/naming.js +52 -3
  140. package/dist/naming.js.map +1 -1
  141. package/dist/payload-codegen.d.ts.map +1 -1
  142. package/dist/payload-codegen.js +14 -6
  143. package/dist/payload-codegen.js.map +1 -1
  144. package/dist/pk-resolver.d.ts.map +1 -1
  145. package/dist/pk-resolver.js +4 -2
  146. package/dist/pk-resolver.js.map +1 -1
  147. package/dist/projection/extract-view-spec.d.ts.map +1 -1
  148. package/dist/projection/extract-view-spec.js +52 -26
  149. package/dist/projection/extract-view-spec.js.map +1 -1
  150. package/dist/relation-resolver.d.ts +16 -0
  151. package/dist/relation-resolver.d.ts.map +1 -1
  152. package/dist/relation-resolver.js +82 -1
  153. package/dist/relation-resolver.js.map +1 -1
  154. package/dist/render-context.d.ts +25 -2
  155. package/dist/render-context.d.ts.map +1 -1
  156. package/dist/render-context.js +7 -0
  157. package/dist/render-context.js.map +1 -1
  158. package/dist/render-engine/embedded-templates.generated.d.ts +2 -0
  159. package/dist/render-engine/embedded-templates.generated.d.ts.map +1 -0
  160. package/dist/render-engine/embedded-templates.generated.js +15 -0
  161. package/dist/render-engine/embedded-templates.generated.js.map +1 -0
  162. package/dist/render-engine/framework-provider.d.ts.map +1 -1
  163. package/dist/render-engine/framework-provider.js +26 -13
  164. package/dist/render-engine/framework-provider.js.map +1 -1
  165. package/dist/runner.d.ts.map +1 -1
  166. package/dist/runner.js +20 -0
  167. package/dist/runner.js.map +1 -1
  168. package/dist/templates/docs-file.d.ts +2 -6
  169. package/dist/templates/docs-file.d.ts.map +1 -1
  170. package/dist/templates/docs-file.js +2 -5
  171. package/dist/templates/docs-file.js.map +1 -1
  172. package/dist/templates/drizzle-schema.d.ts.map +1 -1
  173. package/dist/templates/drizzle-schema.js +72 -23
  174. package/dist/templates/drizzle-schema.js.map +1 -1
  175. package/dist/templates/entity-constants.d.ts +7 -0
  176. package/dist/templates/entity-constants.d.ts.map +1 -1
  177. package/dist/templates/entity-constants.js +3 -3
  178. package/dist/templates/entity-constants.js.map +1 -1
  179. package/dist/templates/entity-file.d.ts.map +1 -1
  180. package/dist/templates/entity-file.js +16 -5
  181. package/dist/templates/entity-file.js.map +1 -1
  182. package/dist/templates/enums-file.d.ts +11 -0
  183. package/dist/templates/enums-file.d.ts.map +1 -0
  184. package/dist/templates/enums-file.js +44 -0
  185. package/dist/templates/enums-file.js.map +1 -0
  186. package/dist/templates/extract-delegate-emitter.d.ts.map +1 -1
  187. package/dist/templates/extract-delegate-emitter.js +6 -8
  188. package/dist/templates/extract-delegate-emitter.js.map +1 -1
  189. package/dist/templates/extractor.d.ts.map +1 -1
  190. package/dist/templates/extractor.js +58 -41
  191. package/dist/templates/extractor.js.map +1 -1
  192. package/dist/templates/field-meta.d.ts.map +1 -1
  193. package/dist/templates/field-meta.js +2 -6
  194. package/dist/templates/field-meta.js.map +1 -1
  195. package/dist/templates/filter-allowlist.d.ts +7 -2
  196. package/dist/templates/filter-allowlist.d.ts.map +1 -1
  197. package/dist/templates/filter-allowlist.js +18 -10
  198. package/dist/templates/filter-allowlist.js.map +1 -1
  199. package/dist/templates/filter-shared.js +2 -2
  200. package/dist/templates/filter-shared.js.map +1 -1
  201. package/dist/templates/filter-type.d.ts +7 -1
  202. package/dist/templates/filter-type.d.ts.map +1 -1
  203. package/dist/templates/filter-type.js +10 -6
  204. package/dist/templates/filter-type.js.map +1 -1
  205. package/dist/templates/find-templates.d.ts +4 -0
  206. package/dist/templates/find-templates.d.ts.map +1 -0
  207. package/dist/templates/find-templates.js +15 -0
  208. package/dist/templates/find-templates.js.map +1 -0
  209. package/dist/templates/fr010-field-mapping.d.ts +2 -0
  210. package/dist/templates/fr010-field-mapping.d.ts.map +1 -1
  211. package/dist/templates/fr010-field-mapping.js +15 -11
  212. package/dist/templates/fr010-field-mapping.js.map +1 -1
  213. package/dist/templates/inferred-types.d.ts +44 -7
  214. package/dist/templates/inferred-types.d.ts.map +1 -1
  215. package/dist/templates/inferred-types.js +121 -19
  216. package/dist/templates/inferred-types.js.map +1 -1
  217. package/dist/templates/mermaid-er.d.ts +35 -2
  218. package/dist/templates/mermaid-er.d.ts.map +1 -1
  219. package/dist/templates/mermaid-er.js +174 -7
  220. package/dist/templates/mermaid-er.js.map +1 -1
  221. package/dist/templates/output-format-spec-emitter.js +1 -1
  222. package/dist/templates/output-format-spec-emitter.js.map +1 -1
  223. package/dist/templates/output-parser.d.ts.map +1 -1
  224. package/dist/templates/output-parser.js +31 -80
  225. package/dist/templates/output-parser.js.map +1 -1
  226. package/dist/templates/output-prompt.d.ts.map +1 -1
  227. package/dist/templates/output-prompt.js +2 -2
  228. package/dist/templates/output-prompt.js.map +1 -1
  229. package/dist/templates/queries-file.d.ts.map +1 -1
  230. package/dist/templates/queries-file.js +113 -5
  231. package/dist/templates/queries-file.js.map +1 -1
  232. package/dist/templates/queries.d.ts +7 -2
  233. package/dist/templates/queries.d.ts.map +1 -1
  234. package/dist/templates/queries.js +15 -15
  235. package/dist/templates/queries.js.map +1 -1
  236. package/dist/templates/relations-block.d.ts.map +1 -1
  237. package/dist/templates/relations-block.js +12 -3
  238. package/dist/templates/relations-block.js.map +1 -1
  239. package/dist/templates/render-helper.d.ts.map +1 -1
  240. package/dist/templates/render-helper.js +5 -5
  241. package/dist/templates/render-helper.js.map +1 -1
  242. package/dist/templates/routes-file-hono.d.ts.map +1 -1
  243. package/dist/templates/routes-file-hono.js +1 -2
  244. package/dist/templates/routes-file-hono.js.map +1 -1
  245. package/dist/templates/routes-file.d.ts.map +1 -1
  246. package/dist/templates/routes-file.js +184 -7
  247. package/dist/templates/routes-file.js.map +1 -1
  248. package/dist/templates/tph-discriminator.d.ts +56 -0
  249. package/dist/templates/tph-discriminator.d.ts.map +1 -0
  250. package/dist/templates/tph-discriminator.js +180 -0
  251. package/dist/templates/tph-discriminator.js.map +1 -0
  252. package/dist/templates/value-object-file.d.ts +2 -1
  253. package/dist/templates/value-object-file.d.ts.map +1 -1
  254. package/dist/templates/value-object-file.js +33 -5
  255. package/dist/templates/value-object-file.js.map +1 -1
  256. package/dist/templates/zod-validators.d.ts +65 -2
  257. package/dist/templates/zod-validators.d.ts.map +1 -1
  258. package/dist/templates/zod-validators.js +202 -22
  259. package/dist/templates/zod-validators.js.map +1 -1
  260. package/package.json +103 -34
  261. package/src/column-mapper.ts +79 -32
  262. package/src/constants.ts +18 -0
  263. package/src/docs-paths.ts +128 -0
  264. package/src/enum-import.ts +43 -0
  265. package/src/enum-shared.ts +95 -0
  266. package/src/generator-registry.ts +204 -0
  267. package/src/generator.ts +6 -0
  268. package/src/generators/api-doc-render.ts +572 -0
  269. package/src/generators/api-docs-file.ts +146 -0
  270. package/src/generators/api-field-shape.ts +114 -0
  271. package/src/generators/api-label.ts +7 -0
  272. package/src/generators/api-model.ts +1067 -0
  273. package/src/generators/docs-data-builder.ts +483 -189
  274. package/src/generators/docs-data.ts +139 -28
  275. package/src/generators/docs-file.ts +205 -39
  276. package/src/generators/entity-file.ts +31 -15
  277. package/src/generators/extractor-file.ts +2 -1
  278. package/src/generators/field-anchor.ts +24 -0
  279. package/src/generators/index.ts +8 -1
  280. package/src/generators/mermaid-er.ts +14 -0
  281. package/src/generators/output-parser-file.ts +3 -4
  282. package/src/generators/output-prompt-file.ts +2 -1
  283. package/src/generators/prompt-render-file.ts +3 -4
  284. package/src/generators/queries-file.ts +9 -3
  285. package/src/generators/render-helper-file.ts +2 -1
  286. package/src/generators/routes-file-hono.ts +5 -1
  287. package/src/generators/routes-file.ts +7 -1
  288. package/src/generators/template-doc-builder.ts +306 -0
  289. package/src/generators/template-doc-data.ts +85 -0
  290. package/src/generators/template-payload-tree.ts +71 -0
  291. package/src/generators/template-source-annotate.ts +290 -0
  292. package/src/generators/template-source-render.ts +203 -0
  293. package/src/generators/trace-helper-file.ts +301 -0
  294. package/src/import-path.ts +28 -0
  295. package/src/index.ts +55 -4
  296. package/src/metaobjects-config.ts +146 -2
  297. package/src/naming.ts +73 -3
  298. package/src/payload-codegen.ts +16 -5
  299. package/src/pk-resolver.ts +4 -2
  300. package/src/projection/extract-view-spec.ts +50 -31
  301. package/src/relation-resolver.ts +103 -1
  302. package/src/render-context.ts +32 -2
  303. package/src/render-engine/embedded-templates.generated.ts +14 -0
  304. package/src/render-engine/framework-provider.ts +25 -11
  305. package/src/runner.ts +24 -0
  306. package/src/templates/docs-file.ts +2 -9
  307. package/src/templates/drizzle-schema.ts +80 -28
  308. package/src/templates/entity-constants.ts +3 -3
  309. package/src/templates/entity-file.ts +16 -5
  310. package/src/templates/enums-file.ts +50 -0
  311. package/src/templates/extract-delegate-emitter.ts +6 -7
  312. package/src/templates/extractor.ts +70 -40
  313. package/src/templates/field-meta.ts +1 -7
  314. package/src/templates/filter-allowlist.ts +18 -11
  315. package/src/templates/filter-shared.ts +2 -2
  316. package/src/templates/filter-type.ts +9 -7
  317. package/src/templates/find-templates.ts +15 -0
  318. package/src/templates/fr010-field-mapping.ts +15 -13
  319. package/src/templates/inferred-types.ts +122 -21
  320. package/src/templates/mermaid-er.ts +176 -8
  321. package/src/templates/output-format-spec-emitter.ts +1 -1
  322. package/src/templates/output-parser.ts +31 -80
  323. package/src/templates/output-prompt.ts +2 -1
  324. package/src/templates/queries-file.ts +133 -4
  325. package/src/templates/queries.ts +21 -15
  326. package/src/templates/relations-block.ts +19 -3
  327. package/src/templates/render-helper.ts +5 -4
  328. package/src/templates/routes-file-hono.ts +1 -2
  329. package/src/templates/routes-file.ts +234 -7
  330. package/src/templates/tph-discriminator.ts +232 -0
  331. package/src/templates/value-object-file.ts +39 -5
  332. package/src/templates/zod-validators.ts +225 -21
  333. package/templates/api/agent-api.md.mustache +30 -0
  334. package/templates/api/entity-api.md.mustache +69 -0
  335. package/templates/api/index.md.mustache +21 -0
  336. package/templates/docs/entity-page.md.mustache +33 -21
  337. package/templates/docs/template-page.md.mustache +56 -0
  338. package/dist/templates/extract-schema-emitter.d.ts +0 -8
  339. package/dist/templates/extract-schema-emitter.d.ts.map +0 -1
  340. package/dist/templates/extract-schema-emitter.js +0 -81
  341. package/dist/templates/extract-schema-emitter.js.map +0 -1
  342. package/src/templates/extract-schema-emitter.ts +0 -111
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metaobjectsdev/codegen-ts",
3
- "version": "0.9.0",
3
+ "version": "0.11.0-rc.1",
4
4
  "description": "TypeScript codegen engine for MetaObjects — emits Drizzle, Zod, and Fastify artifacts.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -20,9 +20,70 @@
20
20
  "bun": "./src/templates/zod-validators.ts",
21
21
  "types": "./dist/templates/zod-validators.d.ts",
22
22
  "default": "./dist/templates/zod-validators.js"
23
+ },
24
+ "./templates/entity-file": {
25
+ "bun": "./src/templates/entity-file.ts",
26
+ "types": "./dist/templates/entity-file.d.ts",
27
+ "default": "./dist/templates/entity-file.js"
28
+ },
29
+ "./templates/drizzle-schema": {
30
+ "bun": "./src/templates/drizzle-schema.ts",
31
+ "types": "./dist/templates/drizzle-schema.d.ts",
32
+ "default": "./dist/templates/drizzle-schema.js"
33
+ },
34
+ "./templates/inferred-types": {
35
+ "bun": "./src/templates/inferred-types.ts",
36
+ "types": "./dist/templates/inferred-types.d.ts",
37
+ "default": "./dist/templates/inferred-types.js"
38
+ },
39
+ "./templates/barrel": {
40
+ "bun": "./src/templates/barrel.ts",
41
+ "types": "./dist/templates/barrel.d.ts",
42
+ "default": "./dist/templates/barrel.js"
43
+ },
44
+ "./templates/filter-type": {
45
+ "bun": "./src/templates/filter-type.ts",
46
+ "types": "./dist/templates/filter-type.d.ts",
47
+ "default": "./dist/templates/filter-type.js"
48
+ },
49
+ "./templates/filter-allowlist": {
50
+ "bun": "./src/templates/filter-allowlist.ts",
51
+ "types": "./dist/templates/filter-allowlist.d.ts",
52
+ "default": "./dist/templates/filter-allowlist.js"
53
+ },
54
+ "./templates/entity-constants": {
55
+ "bun": "./src/templates/entity-constants.ts",
56
+ "types": "./dist/templates/entity-constants.d.ts",
57
+ "default": "./dist/templates/entity-constants.js"
58
+ },
59
+ "./templates/queries-file": {
60
+ "bun": "./src/templates/queries-file.ts",
61
+ "types": "./dist/templates/queries-file.d.ts",
62
+ "default": "./dist/templates/queries-file.js"
63
+ },
64
+ "./templates/routes-file": {
65
+ "bun": "./src/templates/routes-file.ts",
66
+ "types": "./dist/templates/routes-file.d.ts",
67
+ "default": "./dist/templates/routes-file.js"
68
+ },
69
+ "./templates/value-object-file": {
70
+ "bun": "./src/templates/value-object-file.ts",
71
+ "types": "./dist/templates/value-object-file.d.ts",
72
+ "default": "./dist/templates/value-object-file.js"
73
+ },
74
+ "./templates/projection-decl": {
75
+ "bun": "./src/templates/projection-decl.ts",
76
+ "types": "./dist/templates/projection-decl.d.ts",
77
+ "default": "./dist/templates/projection-decl.js"
23
78
  }
24
79
  },
25
- "files": ["dist", "src", "templates", "README.md", "LICENSE"],
80
+ "files": [
81
+ "dist",
82
+ "src",
83
+ "templates",
84
+ "README.md",
85
+ "LICENSE"
86
+ ],
26
87
  "scripts": {
27
88
  "build": "tsc -p .",
28
89
  "typecheck": "tsc -p tsconfig.typecheck.json"
@@ -31,36 +92,44 @@
31
92
  "author": "Doug Mealing <doug@dougmealing.com>",
32
93
  "homepage": "https://metaobjects.dev",
33
94
  "bugs": {
34
- "url": "https://github.com/metaobjectsdev/metaobjects/issues"
35
- },
36
- "repository": {
37
- "type": "git",
38
- "url": "https://github.com/metaobjectsdev/metaobjects.git",
39
- "directory": "server/typescript/packages/codegen-ts"
40
- },
41
- "keywords": ["metaobjects", "codegen", "drizzle", "zod", "fastify", "typescript"],
42
- "publishConfig": {
43
- "access": "public"
44
- },
45
- "dependencies": {
46
- "@metaobjectsdev/metadata": "0.9.0",
47
- "@metaobjectsdev/render": "0.9.0",
48
- "@biomejs/js-api": "^0.7.0",
49
- "@biomejs/wasm-nodejs": "^1.9.4",
50
- "ts-poet": "^6.10.0"
51
- },
52
- "peerDependencies": {
53
- "drizzle-orm": ">=0.36.0",
54
- "zod": ">=3.23.0"
55
- },
56
- "devDependencies": {
57
- "@biomejs/biome": "^1.9.0",
58
- "@metaobjectsdev/codegen-ts-react": "0.9.0",
59
- "@metaobjectsdev/runtime-ts": "0.9.0",
60
- "bun-types": "latest",
61
- "drizzle-orm": "^0.36.0",
62
- "hono": "^4.6.0",
63
- "typescript": "^5.6.0",
64
- "zod": "^3.23.0"
65
- }
95
+ "url": "https://github.com/metaobjectsdev/metaobjects/issues"
96
+ },
97
+ "repository": {
98
+ "type": "git",
99
+ "url": "https://github.com/metaobjectsdev/metaobjects.git",
100
+ "directory": "server/typescript/packages/codegen-ts"
101
+ },
102
+ "keywords": [
103
+ "metaobjects",
104
+ "codegen",
105
+ "drizzle",
106
+ "zod",
107
+ "fastify",
108
+ "typescript"
109
+ ],
110
+ "publishConfig": {
111
+ "access": "public"
112
+ },
113
+ "dependencies": {
114
+ "@metaobjectsdev/metadata": "0.11.0-rc.1",
115
+ "@metaobjectsdev/render": "0.11.0-rc.1",
116
+ "@biomejs/js-api": "^0.7.0",
117
+ "@biomejs/wasm-nodejs": "^1.9.4",
118
+ "ts-poet": "^6.10.0"
119
+ },
120
+ "peerDependencies": {
121
+ "drizzle-orm": ">=0.36.0",
122
+ "zod": ">=3.23.0"
123
+ },
124
+ "devDependencies": {
125
+ "@biomejs/biome": "^1.9.0",
126
+ "@metaobjectsdev/codegen-ts-react": "0.11.0-rc.1",
127
+ "@metaobjectsdev/migrate-ts": "0.11.0-rc.1",
128
+ "@metaobjectsdev/runtime-ts": "0.11.0-rc.1",
129
+ "bun-types": "latest",
130
+ "drizzle-orm": "^0.36.0",
131
+ "hono": "^4.6.0",
132
+ "typescript": "^5.6.0",
133
+ "zod": "^3.23.0"
134
+ }
66
135
  }
@@ -15,7 +15,6 @@ import {
15
15
  FIELD_SUBTYPE_TIME,
16
16
  FIELD_SUBTYPE_TIMESTAMP,
17
17
  FIELD_SUBTYPE_OBJECT,
18
- FIELD_SUBTYPE_CLASS,
19
18
  FIELD_SUBTYPE_ENUM,
20
19
  FIELD_SUBTYPE_UUID,
21
20
  VALIDATOR_SUBTYPE_REQUIRED,
@@ -27,6 +26,8 @@ import {
27
26
  FIELD_ATTR_UNIQUE,
28
27
  FIELD_ATTR_DEFAULT,
29
28
  FIELD_ATTR_OBJECT_REF,
29
+ FIELD_ATTR_STORAGE,
30
+ STORAGE_JSONB,
30
31
  VALIDATOR_ATTR_MAX,
31
32
  FIELD_ATTR_DB_COLUMN_TYPE,
32
33
  DB_COLUMN_TYPE_UUID,
@@ -35,7 +36,7 @@ import {
35
36
  } from "@metaobjectsdev/metadata";
36
37
  import { columnNameFromField } from "./naming.js";
37
38
  import { enumValues } from "./enum-meta.js";
38
- import { DEFAULT_COLUMN_NAMING_STRATEGY } from "@metaobjectsdev/metadata";
39
+ import { DEFAULT_COLUMN_NAMING_STRATEGY, stripPackage } from "@metaobjectsdev/metadata";
39
40
  import type { Dialect, ColumnNamingStrategy } from "./metaobjects-config.js";
40
41
 
41
42
  export type { Dialect };
@@ -82,7 +83,6 @@ function sqliteJsonArrayElementTsType(subType: string): string | undefined {
82
83
  switch (subType) {
83
84
  case FIELD_SUBTYPE_STRING:
84
85
  case FIELD_SUBTYPE_ENUM:
85
- case FIELD_SUBTYPE_CLASS:
86
86
  case FIELD_SUBTYPE_UUID:
87
87
  case FIELD_SUBTYPE_DATE:
88
88
  case FIELD_SUBTYPE_TIME:
@@ -134,15 +134,20 @@ export interface ColumnSpec {
134
134
  checkConstraint?: string;
135
135
  /**
136
136
  * Optional `.$type<...>()` chain target. Renderer (drizzle-schema.ts) emits
137
- * `.$type<TS[]>()` ahead of the modifiers chain, using ts-poet `imp()` for
138
- * objectRef variants so the cross-module type import auto-hoists.
137
+ * it ahead of the modifiers chain, using ts-poet `imp()` for objectRef
138
+ * variants so the cross-module type import auto-hoists. `array` controls the
139
+ * `[]` suffix: a single value (`VO`) vs a collection (`VO[]`).
139
140
  * `kind: "scalar"` covers string[]/number[]/boolean[] — no import needed.
140
- * `kind: "objectRef"` covers SourceLens[]/Dissent[]/etc. — `module` is the
141
- * relative import path to that entity's emitted module.
141
+ * `kind: "objectRef"` covers SourceLens/Dissent/etc. — only the bare VO `name`
142
+ * is carried; the renderer resolves the import MODULE via the shared
143
+ * `valueObjectModuleSpecifier` (layout/package/extStyle-aware, identical to the
144
+ * field's TS type + Zod schema). A single Postgres jsonb object column
145
+ * (`array: false`) gets `.$type<VO>()`; an array of VOs held in one jsonb
146
+ * column gets `.$type<VO[]>()`.
142
147
  */
143
148
  dollarTypeRef?:
144
- | { kind: "scalar"; tsType: "string" | "number" | "boolean" }
145
- | { kind: "objectRef"; name: string; module: string };
149
+ | { kind: "scalar"; tsType: "string" | "number" | "boolean"; array: boolean }
150
+ | { kind: "objectRef"; name: string; array: boolean };
146
151
  }
147
152
 
148
153
  /**
@@ -163,8 +168,9 @@ export interface ColumnSpec {
163
168
  */
164
169
  function pgColumnTypeOverride(
165
170
  field: MetaField,
171
+ timestampMode: "date" | "string" = "string",
166
172
  ): { fnName: string; fnOptions?: Record<string, unknown> } | undefined {
167
- const dbColumnType = field.ownAttr(FIELD_ATTR_DB_COLUMN_TYPE);
173
+ const dbColumnType = field.attr(FIELD_ATTR_DB_COLUMN_TYPE);
168
174
  if (typeof dbColumnType !== "string") return undefined;
169
175
  switch (dbColumnType) {
170
176
  case DB_COLUMN_TYPE_UUID:
@@ -173,9 +179,10 @@ function pgColumnTypeOverride(
173
179
  return { fnName: "jsonb" };
174
180
  case DB_COLUMN_TYPE_TIMESTAMP_WITH_TZ:
175
181
  // Drizzle pg-core: timestamp(col, { withTimezone: true }) → timestamptz.
176
- // mode:"string" for the same reason as the plain-timestamp branch — the
177
- // schema + wire contract carry timestamps as strings.
178
- return { fnName: "timestamp", fnOptions: { mode: "string", withTimezone: true } };
182
+ // mode defaults to "string" (ISO-8601 wire contract, matching the generated
183
+ // Zod); a consumer can opt into "date" (drizzle's native mode) via
184
+ // codegen.timestampMode when its hand-written code works with JS Dates.
185
+ return { fnName: "timestamp", fnOptions: { mode: timestampMode, withTimezone: true } };
179
186
  default:
180
187
  return undefined;
181
188
  }
@@ -184,7 +191,7 @@ function pgColumnTypeOverride(
184
191
  /** Resolve max length from validator.length child or @maxLength attr.
185
192
  * Uses field.validators() (effective) so inherited validators are seen. */
186
193
  function getMaxLength(field: MetaField): number | undefined {
187
- const lenAttr = field.ownAttr(FIELD_ATTR_MAX_LENGTH);
194
+ const lenAttr = field.attr(FIELD_ATTR_MAX_LENGTH);
188
195
  if (typeof lenAttr === "number") return lenAttr;
189
196
  for (const child of field.validators()) {
190
197
  if (child.subType === VALIDATOR_SUBTYPE_LENGTH) {
@@ -198,14 +205,24 @@ function getMaxLength(field: MetaField): number | undefined {
198
205
  /** Check for validator.required child OR @required attr.
199
206
  * Uses field.validators() (effective) so inherited validators are seen. */
200
207
  function isRequired(field: MetaField): boolean {
201
- if (field.ownAttr(FIELD_ATTR_REQUIRED) === true) return true;
208
+ if (field.attr(FIELD_ATTR_REQUIRED) === true) return true;
202
209
  return field.validators().some((child) => child.subType === VALIDATOR_SUBTYPE_REQUIRED);
203
210
  }
204
211
 
212
+ /** The bare (package-stripped) @objectRef name on a field.object, or undefined
213
+ * when unset. Used as the `.$type<VO>()` target + its sibling-module import.
214
+ * A fully-qualified ref (acme::ai::SourceLens) strips to the short name. */
215
+ function objectRefBaseName(field: MetaField): string | undefined {
216
+ const ref = field.attr(FIELD_ATTR_OBJECT_REF);
217
+ if (typeof ref === "string" && ref.length > 0) return stripPackage(ref);
218
+ return undefined;
219
+ }
220
+
205
221
  export function mapColumnType(
206
222
  field: MetaField,
207
223
  dialect: Dialect,
208
224
  strategy: ColumnNamingStrategy = DEFAULT_COLUMN_NAMING_STRATEGY,
225
+ timestampMode: "date" | "string" = "string",
209
226
  ): ColumnSpec {
210
227
  const dbName = field.column ?? columnNameFromField(field.name, strategy);
211
228
  const importModule = dialect === "sqlite" ? "drizzle-orm/sqlite-core" : "drizzle-orm/pg-core";
@@ -243,13 +260,21 @@ export function mapColumnType(
243
260
  // isn't a silent rounding hazard.
244
261
  leadingComment = "TODO: SQLite has no decimal type; stored as text. Convert at the application boundary or migrate to Postgres for native numeric.";
245
262
  break;
263
+ case FIELD_SUBTYPE_OBJECT:
264
+ // A nested object is stored as a single JSON column (the default
265
+ // single-jsonb-column storage). SQLite has no native jsonb, so the
266
+ // idiomatic Drizzle form is text(..., { mode: "json" }) — agreeing with
267
+ // migrate-ts/expected-schema, which maps field.object → { kind: "json" }
268
+ // (JSON on SQLite). @storage flattened expansion is a separate codegen
269
+ // gap; the column TYPE here is the single-column JSON representation.
270
+ fnName = "text";
271
+ fnOptions = { mode: "json" };
272
+ break;
246
273
  case FIELD_SUBTYPE_DATE:
247
274
  case FIELD_SUBTYPE_TIME:
248
275
  case FIELD_SUBTYPE_TIMESTAMP:
249
276
  case FIELD_SUBTYPE_STRING:
250
277
  case FIELD_SUBTYPE_ENUM:
251
- case FIELD_SUBTYPE_CLASS:
252
- case FIELD_SUBTYPE_OBJECT:
253
278
  case FIELD_SUBTYPE_UUID:
254
279
  // SQLite has no native uuid type; store as TEXT (string native binding).
255
280
  fnName = "text";
@@ -263,7 +288,7 @@ export function mapColumnType(
263
288
  // A physical @dbColumnType override wins over the subtype default (Postgres
264
289
  // only; SQLite has no native analogue and falls through above). Resolved
265
290
  // first so the override-precedence matches migrate-ts's expected-schema.
266
- const override = pgColumnTypeOverride(field);
291
+ const override = pgColumnTypeOverride(field, timestampMode);
267
292
  if (override !== undefined) {
268
293
  // Override fully determines the physical type; skip the subtype switch.
269
294
  fnName = override.fnName;
@@ -302,7 +327,7 @@ export function mapColumnType(
302
327
  // inconsistent with the string-typed schema + wire contract and
303
328
  // throws on a string write. See SP-B api-contract-generated lane.
304
329
  fnName = "timestamp";
305
- fnOptions = { mode: "string" };
330
+ fnOptions = { mode: timestampMode };
306
331
  break;
307
332
  case FIELD_SUBTYPE_UUID:
308
333
  // Postgres native uuid column; native TS binding stays `string`.
@@ -314,8 +339,8 @@ export function mapColumnType(
314
339
  // declared @precision/@scale (mirroring migrate-ts/expected-schema so
315
340
  // codegen and the DDL agree), falling back to a sane default.
316
341
  fnName = "numeric";
317
- const precision = field.ownAttr(FIELD_ATTR_PRECISION);
318
- const scale = field.ownAttr(FIELD_ATTR_SCALE);
342
+ const precision = field.attr(FIELD_ATTR_PRECISION);
343
+ const scale = field.attr(FIELD_ATTR_SCALE);
319
344
  if (typeof precision === "number" && typeof scale === "number") {
320
345
  fnOptions = { precision, scale };
321
346
  } else if (typeof precision === "number") {
@@ -335,9 +360,15 @@ export function mapColumnType(
335
360
  }
336
361
  break;
337
362
  }
338
- case FIELD_SUBTYPE_ENUM:
339
- case FIELD_SUBTYPE_CLASS:
340
363
  case FIELD_SUBTYPE_OBJECT:
364
+ // A nested object is stored as a single jsonb column (the default
365
+ // single-jsonb-column storage), matching migrate-ts/expected-schema,
366
+ // which maps field.object → { kind: "json" } → JSONB on Postgres.
367
+ // @storage flattened expansion is a separate codegen gap; the column
368
+ // TYPE here is the single-column jsonb representation.
369
+ fnName = "jsonb";
370
+ break;
371
+ case FIELD_SUBTYPE_ENUM:
341
372
  default:
342
373
  fnName = "text";
343
374
  break;
@@ -360,7 +391,11 @@ export function mapColumnType(
360
391
 
361
392
  const modifiers: string[] = [];
362
393
 
363
- if (dialect === "postgres" && isArray) {
394
+ // Postgres native arrays (text[]/integer[]/…) apply to SCALAR array fields
395
+ // only. An object-typed field is stored as a single jsonb column holding the
396
+ // JSON array (storage jsonb/subdocument), so it gets NO native .array() — the
397
+ // array-ness is carried by the .$type<VO[]>() annotation computed below.
398
+ if (dialect === "postgres" && isArray && subType !== FIELD_SUBTYPE_OBJECT) {
364
399
  modifiers.push(".array()");
365
400
  }
366
401
 
@@ -379,12 +414,12 @@ export function mapColumnType(
379
414
  modifiers.push(".notNull()");
380
415
  }
381
416
 
382
- if (field.ownAttr(FIELD_ATTR_UNIQUE) === true) {
417
+ if (field.attr(FIELD_ATTR_UNIQUE) === true) {
383
418
  modifiers.push(".unique()");
384
419
  }
385
420
 
386
421
  let defaultExpr: DefaultExpr | undefined;
387
- const defaultAttr = field.ownAttr(FIELD_ATTR_DEFAULT);
422
+ const defaultAttr = field.attr(FIELD_ATTR_DEFAULT);
388
423
  if (defaultAttr !== undefined) {
389
424
  // SQL-expression detection runs on the raw string value — a string like
390
425
  // "CURRENT_TIMESTAMP" or "now" must be emitted as sql`...`, not a literal.
@@ -407,21 +442,33 @@ export function mapColumnType(
407
442
  }
408
443
  }
409
444
 
410
- // SQLite isArray columns route through {mode:"json"} above; compute the
411
- // $type<E[]>() target so the renderer can hoist any cross-module imports.
445
+ // jsonb / JSON-in-text columns infer as `unknown` in Drizzle without a
446
+ // .$type<>() annotation. Carry the logical TS type so the column is typed:
447
+ // - SQLite isArray: arrays serialize as JSON-in-text → .$type<E[]>() (scalar
448
+ // element type, or the @objectRef VO for object arrays).
449
+ // - Postgres field.object: a single jsonb column → .$type<VO>(), or
450
+ // .$type<VO[]>() when isArray (the JSON array lives in the one column; no
451
+ // native .array() is emitted for object storage — see modifiers above).
452
+ // Scalar Postgres arrays use native .array() (already element-typed by
453
+ // Drizzle) so they need no $type.
412
454
  let dollarTypeRef: ColumnSpec["dollarTypeRef"];
413
455
  if (dialect === "sqlite" && isArray) {
414
456
  if (subType === FIELD_SUBTYPE_OBJECT) {
415
- const ref = field.ownAttr(FIELD_ATTR_OBJECT_REF);
416
- if (typeof ref === "string" && ref.length > 0) {
417
- dollarTypeRef = { kind: "objectRef", name: ref, module: `./${ref}.js` };
457
+ const base = objectRefBaseName(field);
458
+ if (base !== undefined) {
459
+ dollarTypeRef = { kind: "objectRef", name: base, array: true };
418
460
  }
419
461
  } else {
420
462
  const scalar = sqliteJsonArrayElementTsType(subType);
421
463
  if (scalar !== undefined) {
422
- dollarTypeRef = { kind: "scalar", tsType: scalar as "string" | "number" | "boolean" };
464
+ dollarTypeRef = { kind: "scalar", tsType: scalar as "string" | "number" | "boolean", array: true };
423
465
  }
424
466
  }
467
+ } else if (dialect === "postgres" && subType === FIELD_SUBTYPE_OBJECT) {
468
+ const base = objectRefBaseName(field);
469
+ if (base !== undefined) {
470
+ dollarTypeRef = { kind: "objectRef", name: base, array: isArray };
471
+ }
425
472
  }
426
473
 
427
474
  const result: ColumnSpec = {
package/src/constants.ts CHANGED
@@ -8,3 +8,21 @@ export const EXTRA_SUFFIX = ".extra";
8
8
 
9
9
  /** Default outDir used by tests + as a sane default for generate(). */
10
10
  export const DEFAULT_OUT_DIR = "./src/db/entities";
11
+
12
+ // ---------------------------------------------------------------------------
13
+ // Codegen-control attributes.
14
+ //
15
+ // These are per-entity opt-in/opt-out flags read by generators (NOT metamodel
16
+ // vocabulary — they tune codegen, not the model). Named here so the literals
17
+ // aren't scattered as magic strings across the generator packages (compile-time
18
+ // typo safety), matching the metadata package's constants discipline.
19
+ // ---------------------------------------------------------------------------
20
+
21
+ /** `@emitTanstack: false` — skip the TanStack hooks + grid generators for an entity. */
22
+ export const CODEGEN_ATTR_EMIT_TANSTACK = "emitTanstack";
23
+ /** `@emitGrid: true` — opt a TPH subtype IN to its own per-subtype grid (default: the polymorphic base grid is the single source). */
24
+ export const CODEGEN_ATTR_EMIT_GRID = "emitGrid";
25
+ /** `@emitForm: false` — skip the React form generator for an entity. */
26
+ export const CODEGEN_ATTR_EMIT_FORM = "emitForm";
27
+ /** `@emitRoutes: false` — skip the Fastify routes generator for an entity. */
28
+ export const CODEGEN_ATTR_EMIT_ROUTES = "emitRoutes";
@@ -0,0 +1,128 @@
1
+ // docs-paths.ts — the SINGLE source of truth for where a docs page is written
2
+ // AND how one page links to another. The file location and every inbound link
3
+ // href are derived from the SAME functions here, so they can never diverge.
4
+ //
5
+ // Why this matters: pages are placed by short name. In "flat" layout two nodes
6
+ // that share a short name across different packages (e.g. `acme::sales::Order`
7
+ // and `acme::billing::Order`) would both want `Order.md` and one would silently
8
+ // overwrite the other. `assertNoDuplicateDocPaths()` is the hard backstop that
9
+ // turns that data-loss into a clear error; "package" layout folds pages under
10
+ // package-path subdirs (`acme/sales/Order.md`) so multi-package models work.
11
+
12
+ import { PACKAGE_SEPARATOR } from "@metaobjectsdev/metadata";
13
+ import { relative as posixRelative } from "node:path/posix";
14
+ import { packageToPath, type OutputLayout } from "./import-path.js";
15
+
16
+ /** The minimal shape needed to place a docs page / compute a link to it: a
17
+ * short name and its EFFECTIVE package. Build one from a metadata node via
18
+ * `docPageNode()`. */
19
+ export interface DocPageNode {
20
+ readonly name: string;
21
+ readonly package?: string | undefined;
22
+ }
23
+
24
+ /** A metadata node enough to derive page placement. `resolutionKey()` carries
25
+ * the EFFECTIVE package (own package OR the file-default captured at parse
26
+ * time) folded as `<pkg>::<name>` — `.package` alone is often undefined for
27
+ * objects (FR5d keeps object fqn() bare), so we read placement off the
28
+ * resolution key instead. */
29
+ interface PlaceableNode {
30
+ readonly name: string;
31
+ resolutionKey(): string;
32
+ }
33
+
34
+ /** Effective package of a placeable node: the prefix of `resolutionKey()`
35
+ * before the trailing `::<name>`, or undefined when the node is package-less. */
36
+ export function effectivePackage(node: PlaceableNode): string | undefined {
37
+ const key = node.resolutionKey();
38
+ const suffix = `${PACKAGE_SEPARATOR}${node.name}`;
39
+ if (key === node.name) return undefined;
40
+ if (key.endsWith(suffix)) {
41
+ const pkg = key.slice(0, key.length - suffix.length);
42
+ return pkg === "" ? undefined : pkg;
43
+ }
44
+ return undefined;
45
+ }
46
+
47
+ /** Build a placement node ({name, effective package}) from a metadata node. The
48
+ * single bridge from a loaded node to the path/href helpers — so file location
49
+ * and link href derive from the SAME effective package. */
50
+ export function docPageNode(node: PlaceableNode): DocPageNode {
51
+ return { name: node.name, package: effectivePackage(node) };
52
+ }
53
+
54
+ /** Output path (relative to the docs out dir) for a node's `.md` page.
55
+ * Flat → `<name>.md` (today's value, byte-identical). Package → folded under
56
+ * the package path (`acme/sales/Order.md`); a package-less node stays at root. */
57
+ export function docPageOutputPath(layout: OutputLayout, node: DocPageNode): string {
58
+ const filename = `${node.name}.md`;
59
+ if (layout === "flat") return filename;
60
+ const dir = packageToPath(node.package);
61
+ return dir === "" ? filename : `${dir}/${filename}`;
62
+ }
63
+
64
+ /** Relative href FROM `fromNode`'s page TO `toNode`'s page. Derived from the
65
+ * same `docPageOutputPath()` placement, so a link always points at the file's
66
+ * real location in BOTH layouts. Flat → `./<to>.md`; package → a correct
67
+ * relative path (e.g. `../comms/OrderEmail.md`). */
68
+ export function docPageHref(
69
+ layout: OutputLayout,
70
+ fromNode: DocPageNode,
71
+ toNode: DocPageNode,
72
+ ): string {
73
+ const toPath = docPageOutputPath(layout, toNode);
74
+ if (layout === "flat") return `./${toPath}`;
75
+ // Relative path from the FROM page's directory to the TO page — the same
76
+ // raw-path rule surfaceCrossHref uses, so the two can never diverge.
77
+ return surfaceCrossHref(docPageOutputPath(layout, fromNode), toPath);
78
+ }
79
+
80
+ /** Relative href between two doc pages whose output paths (relative to the shared
81
+ * docs outDir) may sit under different surface sub-roots — e.g. model `Order.md`
82
+ * and api `api/Order.md`. The shared relative-path rule, over raw paths;
83
+ * docPageHref delegates its package-layout branch here. */
84
+ export function surfaceCrossHref(fromOutputPath: string, toOutputPath: string): string {
85
+ const fromDir = fromOutputPath.includes("/") ? fromOutputPath.slice(0, fromOutputPath.lastIndexOf("/")) : "";
86
+ const rel = posixRelative(fromDir, toOutputPath);
87
+ return rel.startsWith(".") ? rel : `./${rel}`;
88
+ }
89
+
90
+ /** Href FROM a page (at `fromOutputPath`, relative to the docs root) TO a page
91
+ * (`page`, relative to the surface's own root) in an api surface. Relative via
92
+ * `surfaceCrossHref` when the surface is in the same tree; absolute `baseUrl/page`
93
+ * when the surface declares a baseUrl (federated / separate repo). */
94
+ export function apiSurfaceHref(
95
+ fromOutputPath: string,
96
+ surface: { subDir: string; baseUrl?: string },
97
+ page: string,
98
+ ): string {
99
+ if (surface.baseUrl !== undefined && surface.baseUrl !== "") {
100
+ return `${surface.baseUrl.replace(/\/$/, "")}/${page}`;
101
+ }
102
+ return surfaceCrossHref(fromOutputPath, `${surface.subDir}/${page}`);
103
+ }
104
+
105
+ /** A page about to be emitted, paired with the FQN of the node that produced it
106
+ * (for a precise collision diagnostic). */
107
+ export interface DocPagePlacement {
108
+ path: string;
109
+ fqn: string;
110
+ }
111
+
112
+ /** Hard backstop against silent overwrite (ALL layouts): if two placements
113
+ * resolve to the SAME output path, THROW naming both colliding node FQNs and
114
+ * the path. Guarantees a docs run never silently drops a page. */
115
+ export function assertNoDuplicateDocPaths(placements: DocPagePlacement[]): void {
116
+ const seen = new Map<string, string>();
117
+ for (const { path, fqn } of placements) {
118
+ const prior = seen.get(path);
119
+ if (prior !== undefined) {
120
+ throw new Error(
121
+ `docs: duplicate output path "${path}" from nodes ${prior} and ${fqn} — ` +
122
+ `use package layout (outputLayout: "package" / meta docs --layout package) ` +
123
+ `to disambiguate.`,
124
+ );
125
+ }
126
+ seen.set(path, fqn);
127
+ }
128
+ }
@@ -0,0 +1,43 @@
1
+ // FR-019 — import-specifier resolution for shared + provided enums.
2
+ //
3
+ // A consuming entity file references a shared enum `E` by importing it:
4
+ // • materialized (non-@provided) → from the generated shared enums module
5
+ // (`./enums`, at the entity-module target root, package-layout-aware).
6
+ // • @provided → from the per-port-configured module
7
+ // (`ctx.providedEnumModule`). Missing config ⇒ a codegen-time error naming
8
+ // the enum + the config key (ADR-0026: namespace is config, not metadata).
9
+
10
+ import { withExt } from "./render-context.js";
11
+ import type { RenderContext } from "./render-context.js";
12
+ import { relativeModuleSpecifier } from "./import-path.js";
13
+ import { SHARED_ENUMS_BASENAME } from "./templates/enums-file.js";
14
+
15
+ /**
16
+ * Specifier to import a MATERIALIZED shared enum into an entity file in
17
+ * `entityPkg`. The shared module sits at the entity-module target root; in
18
+ * package layout the entity may be nested, so the `./enums` base is adjusted by
19
+ * package depth.
20
+ */
21
+ export function sharedEnumImportSpecifier(
22
+ ctx: RenderContext,
23
+ entityPkg: string | undefined,
24
+ ): string {
25
+ const base = withExt(`./${SHARED_ENUMS_BASENAME}`, ctx.extStyle);
26
+ return relativeModuleSpecifier(ctx.selfTarget.outputLayout, entityPkg, base);
27
+ }
28
+
29
+ /**
30
+ * Specifier to import an externally-PROVIDED enum. Resolved from codegen config
31
+ * (`providedEnumModule`); throws a clear codegen-time error when unset.
32
+ */
33
+ export function providedEnumImportSpecifier(ctx: RenderContext, enumName: string): string {
34
+ const mod = ctx.providedEnumModule;
35
+ if (mod === undefined || mod === "") {
36
+ throw new Error(
37
+ `provided enum "${enumName}" is marked @provided but no module is configured ` +
38
+ `to import it from. Set "providedEnumModule" in your codegen config (e.g. ` +
39
+ `providedEnumModule: "@your-app/enums") so the generated code can reference "${enumName}".`,
40
+ );
41
+ }
42
+ return mod;
43
+ }