@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
@@ -8,8 +8,6 @@ import {
8
8
  FIELD_SUBTYPE_STRING,
9
9
  FIELD_SUBTYPE_INT,
10
10
  FIELD_SUBTYPE_LONG,
11
- FIELD_SUBTYPE_SHORT,
12
- FIELD_SUBTYPE_BYTE,
13
11
  FIELD_SUBTYPE_DOUBLE,
14
12
  FIELD_SUBTYPE_FLOAT,
15
13
  FIELD_SUBTYPE_DECIMAL,
@@ -54,8 +52,6 @@ function defaultViewForSubType(subType: string): string {
54
52
  return VIEW_SUBTYPE_CHECKBOX;
55
53
  case FIELD_SUBTYPE_INT:
56
54
  case FIELD_SUBTYPE_LONG:
57
- case FIELD_SUBTYPE_SHORT:
58
- case FIELD_SUBTYPE_BYTE:
59
55
  case FIELD_SUBTYPE_DOUBLE:
60
56
  case FIELD_SUBTYPE_FLOAT:
61
57
  case FIELD_SUBTYPE_DECIMAL:
@@ -92,8 +88,6 @@ export function zodTypeFor(field: MetaField): string {
92
88
  return "z.string()";
93
89
  case FIELD_SUBTYPE_INT:
94
90
  case FIELD_SUBTYPE_LONG:
95
- case FIELD_SUBTYPE_SHORT:
96
- case FIELD_SUBTYPE_BYTE:
97
91
  case FIELD_SUBTYPE_CURRENCY:
98
92
  return "z.number().int()";
99
93
  case FIELD_SUBTYPE_DOUBLE:
@@ -120,7 +114,7 @@ export function zodTypeFor(field: MetaField): string {
120
114
  export function currencyMetaFor(field: MetaField): { currency: string; locale: string } | null {
121
115
  if (field.subType !== FIELD_SUBTYPE_CURRENCY) return null;
122
116
  const currency =
123
- (field.ownAttr(FIELD_ATTR_CURRENCY) as string | undefined) ?? FIELD_ATTR_CURRENCY_DEFAULT;
117
+ (field.attr(FIELD_ATTR_CURRENCY) as string | undefined) ?? FIELD_ATTR_CURRENCY_DEFAULT;
124
118
  const viewChild = field.views().find((c) => c.subType === VIEW_SUBTYPE_CURRENCY);
125
119
  const locale =
126
120
  (viewChild?.ownAttr(VIEW_CURRENCY_ATTR_LOCALE) as string | undefined) ??
@@ -5,8 +5,6 @@ import {
5
5
  FIELD_ATTR_SORTABLE_DEFAULT_ORDER,
6
6
  FIELD_SUBTYPE_BOOLEAN,
7
7
  FIELD_SUBTYPE_INT,
8
- FIELD_SUBTYPE_SHORT,
9
- FIELD_SUBTYPE_BYTE,
10
8
  FIELD_SUBTYPE_LONG,
11
9
  FIELD_SUBTYPE_DOUBLE,
12
10
  FIELD_SUBTYPE_FLOAT,
@@ -14,18 +12,19 @@ import {
14
12
  FIELD_SUBTYPE_DATE,
15
13
  FIELD_SUBTYPE_TIME,
16
14
  FIELD_SUBTYPE_TIMESTAMP,
15
+ FIELD_SUBTYPE_CURRENCY,
17
16
  opsForSubType,
18
17
  } from "@metaobjectsdev/metadata";
19
18
  import { sortableFields } from "./filter-shared.js";
20
19
 
21
20
  const NUMBER_SUBTYPES = new Set<string>([
22
21
  FIELD_SUBTYPE_INT,
23
- FIELD_SUBTYPE_SHORT,
24
- FIELD_SUBTYPE_BYTE,
25
22
  FIELD_SUBTYPE_LONG,
26
23
  FIELD_SUBTYPE_DOUBLE,
27
24
  FIELD_SUBTYPE_FLOAT,
28
25
  FIELD_SUBTYPE_DECIMAL,
26
+ // currency is integer minor units — coerces as a number on the wire.
27
+ FIELD_SUBTYPE_CURRENCY,
29
28
  ]);
30
29
 
31
30
  const DATETIME_SUBTYPES = new Set<string>([
@@ -42,13 +41,20 @@ function filterSubTypeFor(fieldSubType: string): "string" | "number" | "boolean"
42
41
  return "string";
43
42
  }
44
43
 
45
- function filterableFields(entity: MetaObject): MetaField[] {
44
+ function filterableFields(entity: MetaObject, exclude?: string): MetaField[] {
46
45
  // fields() returns effective fields, so inherited fields (from extends:/super:) are included in allowlists.
47
- return entity.fields().filter((c) => c.ownAttr(FIELD_ATTR_FILTERABLE) === true);
46
+ return entity
47
+ .fields()
48
+ .filter((c) => c.attr(FIELD_ATTR_FILTERABLE) === true && c.name !== exclude);
48
49
  }
49
50
 
50
- export function renderFilterAllowlist(entity: MetaObject): Code {
51
- const fields = filterableFields(entity);
51
+ /**
52
+ * `exclude` (FR-017): drop a field from the allowlist. Used by per-subtype TPH
53
+ * allowlists to omit the discriminator — it's pinned by the per-subtype route
54
+ * path, so a client must not filter on it.
55
+ */
56
+ export function renderFilterAllowlist(entity: MetaObject, exclude?: string): Code {
57
+ const fields = filterableFields(entity, exclude);
52
58
  if (fields.length === 0) {
53
59
  return code`
54
60
  import type { FilterAllowlist } from "@metaobjectsdev/runtime-ts/drizzle-fastify";
@@ -72,11 +78,12 @@ ${rows}
72
78
  `;
73
79
  }
74
80
 
75
- export function renderSortAllowlist(entity: MetaObject): Code {
81
+ export function renderSortAllowlist(entity: MetaObject, exclude?: string): Code {
76
82
  // Sortable = explicit @sortable === true, OR (no @sortable AND @filterable === true).
77
83
  // @sortable: false explicitly opts out.
78
84
  // Uses shared isSortableField predicate — must stay in sync with renderFilterType.
79
- const sortable = sortableFields(entity);
85
+ // `exclude` (FR-017): per-subtype TPH allowlists omit the discriminator.
86
+ const sortable = sortableFields(entity).filter((f) => f.name !== exclude);
80
87
  if (sortable.length === 0) {
81
88
  return code`
82
89
  import type { SortAllowlist } from "@metaobjectsdev/runtime-ts/drizzle-fastify";
@@ -86,7 +93,7 @@ export const ${entity.name}SortAllowlist = {} as const satisfies SortAllowlist;
86
93
  }
87
94
  const rows = sortable
88
95
  .map((f) => {
89
- const defaultOrder = f.ownAttr(FIELD_ATTR_SORTABLE_DEFAULT_ORDER) as string | undefined;
96
+ const defaultOrder = f.attr(FIELD_ATTR_SORTABLE_DEFAULT_ORDER) as string | undefined;
90
97
  const rule =
91
98
  defaultOrder === "asc" || defaultOrder === "desc"
92
99
  ? `{ defaultOrder: ${JSON.stringify(defaultOrder)} as const }`
@@ -15,10 +15,10 @@ import { FIELD_ATTR_FILTERABLE, FIELD_ATTR_SORTABLE } from "@metaobjectsdev/meta
15
15
  * 3. no @sortable → sortable iff @filterable === true
16
16
  */
17
17
  export function isSortableField(field: MetaField): boolean {
18
- const sortableAttr = field.ownAttr(FIELD_ATTR_SORTABLE);
18
+ const sortableAttr = field.attr(FIELD_ATTR_SORTABLE);
19
19
  if (sortableAttr === true) return true;
20
20
  if (sortableAttr === false) return false;
21
- return field.ownAttr(FIELD_ATTR_FILTERABLE) === true;
21
+ return field.attr(FIELD_ATTR_FILTERABLE) === true;
22
22
  }
23
23
 
24
24
  /**
@@ -8,8 +8,6 @@ import {
8
8
  FIELD_ATTR_FILTERABLE,
9
9
  FIELD_SUBTYPE_BOOLEAN,
10
10
  FIELD_SUBTYPE_INT,
11
- FIELD_SUBTYPE_SHORT,
12
- FIELD_SUBTYPE_BYTE,
13
11
  FIELD_SUBTYPE_LONG,
14
12
  FIELD_SUBTYPE_DOUBLE,
15
13
  FIELD_SUBTYPE_FLOAT,
@@ -23,8 +21,6 @@ import { isSortableField } from "./filter-shared.js";
23
21
  // matching the entity field representation (exact decimal string, not lossy number).
24
22
  const NUMBER_VALUE_SUBTYPES = new Set<string>([
25
23
  FIELD_SUBTYPE_INT,
26
- FIELD_SUBTYPE_SHORT,
27
- FIELD_SUBTYPE_BYTE,
28
24
  FIELD_SUBTYPE_LONG,
29
25
  FIELD_SUBTYPE_DOUBLE,
30
26
  FIELD_SUBTYPE_FLOAT,
@@ -49,10 +45,16 @@ function renderFieldUnion(field: MetaField): string {
49
45
  return `${tsName} | { ${opEntries.join("; ")} }`;
50
46
  }
51
47
 
52
- export function renderFilterType(entity: MetaObject): Code {
48
+ /**
49
+ * `exclude` (FR-017): drop a field from the client filter type. Used by
50
+ * per-subtype TPH filter types to omit the discriminator (it's pinned by the
51
+ * per-subtype route path), keeping the client `<Sub>Filter` type aligned with
52
+ * the server's per-subtype allowlist.
53
+ */
54
+ export function renderFilterType(entity: MetaObject, exclude?: string): Code {
53
55
  // fields() returns effective fields, so inherited fields (from extends:/super:) are included in filter types.
54
- const allFields = entity.fields();
55
- const filterableFieldsList = allFields.filter((c) => c.ownAttr(FIELD_ATTR_FILTERABLE) === true);
56
+ const allFields = entity.fields().filter((c) => c.name !== exclude);
57
+ const filterableFieldsList = allFields.filter((c) => c.attr(FIELD_ATTR_FILTERABLE) === true);
56
58
  // Sort union uses isSortableField — same predicate as renderSortAllowlist to prevent
57
59
  // client/server mismatches (@filterable: true + @sortable: false must be excluded from both).
58
60
  const sortFieldNames = allFields.filter(isSortableField).map((f) => `"${f.name}"`);
@@ -0,0 +1,15 @@
1
+ import type { MetaData } from "@metaobjectsdev/metadata";
2
+ import { TYPE_TEMPLATE } from "@metaobjectsdev/metadata";
3
+
4
+ /** All template nodes of `subType` anywhere in the tree (top-level OR nested in entities). */
5
+ export function findTemplates(root: MetaData, subType: string): MetaData[] {
6
+ const out: MetaData[] = [];
7
+ const visit = (node: MetaData) => {
8
+ for (const child of node.ownChildren()) {
9
+ if (child.type === TYPE_TEMPLATE && child.subType === subType) out.push(child);
10
+ visit(child);
11
+ }
12
+ };
13
+ visit(root);
14
+ return out;
15
+ }
@@ -1,7 +1,7 @@
1
1
  // server/typescript/packages/codegen-ts/src/templates/fr010-field-mapping.ts
2
2
  //
3
- // Shared field-kind mapping for the FR-010 codegen emitters (extract-schema-emitter +
4
- // output-format-spec-emitter). Maps a metadata field subtype onto the render engine's
3
+ // Shared field-kind mapping for the FR-010 codegen emitters (the output-format-spec
4
+ // emitter et al.). Maps a metadata field subtype onto the render engine's
5
5
  // FieldKind member, the idiomatic nullable TS type used by the extract mirror interface,
6
6
  // and the ExtractMap accessor that reads it from the forgiving outcome map.
7
7
  //
@@ -13,14 +13,11 @@ import {
13
13
  type MetaData,
14
14
  TYPE_FIELD,
15
15
  FIELD_SUBTYPE_STRING,
16
- FIELD_SUBTYPE_CLASS,
17
16
  FIELD_SUBTYPE_UUID,
18
17
  FIELD_SUBTYPE_DATE,
19
18
  FIELD_SUBTYPE_TIME,
20
19
  FIELD_SUBTYPE_TIMESTAMP,
21
20
  FIELD_SUBTYPE_INT,
22
- FIELD_SUBTYPE_SHORT,
23
- FIELD_SUBTYPE_BYTE,
24
21
  FIELD_SUBTYPE_LONG,
25
22
  FIELD_SUBTYPE_CURRENCY,
26
23
  FIELD_SUBTYPE_DOUBLE,
@@ -34,6 +31,7 @@ import {
34
31
  FIELD_ATTR_COERCE_DEFAULT,
35
32
  FIELD_ATTR_DEFAULT,
36
33
  FIELD_ATTR_NORMALIZE,
34
+ FIELD_ATTR_XML_TEXT,
37
35
  NORMALIZE_DEFAULT,
38
36
  type NormalizeMode,
39
37
  } from "@metaobjectsdev/metadata";
@@ -42,7 +40,6 @@ import {
42
40
  export function scalarKind(subType: string): string | null {
43
41
  switch (subType) {
44
42
  case FIELD_SUBTYPE_STRING:
45
- case FIELD_SUBTYPE_CLASS:
46
43
  case FIELD_SUBTYPE_UUID:
47
44
  case FIELD_SUBTYPE_DATE:
48
45
  case FIELD_SUBTYPE_TIME:
@@ -53,8 +50,6 @@ export function scalarKind(subType: string): string | null {
53
50
  case FIELD_SUBTYPE_DECIMAL:
54
51
  return "STRING";
55
52
  case FIELD_SUBTYPE_INT:
56
- case FIELD_SUBTYPE_SHORT:
57
- case FIELD_SUBTYPE_BYTE:
58
53
  return "INT";
59
54
  case FIELD_SUBTYPE_LONG:
60
55
  case FIELD_SUBTYPE_CURRENCY:
@@ -81,14 +76,21 @@ export function isArray(field: MetaData): boolean {
81
76
 
82
77
  /** True iff the field's @required is explicitly true (or the string "true"). */
83
78
  export function isRequired(field: MetaData): boolean {
84
- const v = field.ownAttr(FIELD_ATTR_REQUIRED);
79
+ const v = field.attr(FIELD_ATTR_REQUIRED);
80
+ if (v === true) return true;
81
+ return typeof v === "string" && v.toLowerCase() === "true";
82
+ }
83
+
84
+ /** True iff the field's @xmlText is explicitly true (the XML text-content extract marker). */
85
+ export function xmlText(field: MetaData): boolean {
86
+ const v = field.attr(FIELD_ATTR_XML_TEXT);
85
87
  if (v === true) return true;
86
88
  return typeof v === "string" && v.toLowerCase() === "true";
87
89
  }
88
90
 
89
91
  /** The string members of an enum field's @values attr (empty when absent). */
90
92
  export function enumValues(field: MetaData): string[] {
91
- const v = field.ownAttr(FIELD_ATTR_VALUES);
93
+ const v = field.attr(FIELD_ATTR_VALUES);
92
94
  if (Array.isArray(v)) return v.map((e) => String(e));
93
95
  return [];
94
96
  }
@@ -98,7 +100,7 @@ export function enumValues(field: MetaData): string[] {
98
100
  * or null when absent. Read own-attr only — `@coerceDefault` is concrete, never inherited.
99
101
  */
100
102
  export function coerceDefault(field: MetaData): string | null {
101
- const v = field.ownAttr(FIELD_ATTR_COERCE_DEFAULT);
103
+ const v = field.attr(FIELD_ATTR_COERCE_DEFAULT);
102
104
  return typeof v === "string" && v.length > 0 ? v : null;
103
105
  }
104
106
 
@@ -106,7 +108,7 @@ export function coerceDefault(field: MetaData): string | null {
106
108
  * FR-011: the field's `@default` member symbol (absent-fill enum value), or null when absent.
107
109
  */
108
110
  export function defaultValue(field: MetaData): string | null {
109
- const v = field.ownAttr(FIELD_ATTR_DEFAULT);
111
+ const v = field.attr(FIELD_ATTR_DEFAULT);
110
112
  return typeof v === "string" && v.length > 0 ? v : null;
111
113
  }
112
114
 
@@ -126,7 +128,7 @@ export function resolveNormalize(field: MetaData, ownerObject: MetaData | null):
126
128
 
127
129
  /** The `@normalize` attr of a node as a NormalizeMode, or null when absent. */
128
130
  function normalizeAttrOf(node: MetaData): NormalizeMode | null {
129
- const v = node.ownAttr(FIELD_ATTR_NORMALIZE);
131
+ const v = node.attr(FIELD_ATTR_NORMALIZE);
130
132
  return typeof v === "string" && v.length > 0 ? (v as NormalizeMode) : null;
131
133
  }
132
134
 
@@ -14,8 +14,6 @@ import {
14
14
  FIELD_SUBTYPE_OBJECT,
15
15
  FIELD_SUBTYPE_STRING,
16
16
  FIELD_SUBTYPE_INT,
17
- FIELD_SUBTYPE_SHORT,
18
- FIELD_SUBTYPE_BYTE,
19
17
  FIELD_SUBTYPE_LONG,
20
18
  FIELD_SUBTYPE_DOUBLE,
21
19
  FIELD_SUBTYPE_FLOAT,
@@ -25,23 +23,41 @@ import {
25
23
  FIELD_SUBTYPE_DATE,
26
24
  FIELD_SUBTYPE_TIME,
27
25
  FIELD_SUBTYPE_TIMESTAMP,
28
- FIELD_SUBTYPE_CLASS,
29
26
  FIELD_SUBTYPE_UUID,
30
27
  FIELD_ATTR_REQUIRED,
31
28
  FIELD_ATTR_OBJECT_REF,
32
29
  } from "@metaobjectsdev/metadata";
33
30
  import { variableNameFromEntity, toPascalCase } from "../naming.js";
31
+ import { valueObjectModuleSpecifier } from "../import-path.js";
32
+ import { stripPackage } from "@metaobjectsdev/metadata";
34
33
  import { enumValues } from "../enum-meta.js";
35
34
  import { renderDocsFor } from "./jsdoc.js";
35
+ import { sharedEnumForField } from "../enum-shared.js";
36
+ import { sharedEnumImportSpecifier, providedEnumImportSpecifier } from "../enum-import.js";
37
+ import type { RenderContext } from "../render-context.js";
36
38
 
37
- export function renderInferredTypes(entity: MetaObject): Code {
38
- const varName = variableNameFromEntity(entity.name);
39
+ /**
40
+ * Emit Drizzle's InferSelectModel / InferInsertModel aliases for an entity.
41
+ *
42
+ * `tphBase` (FR-017): when this entity is a TPH discriminator base, the
43
+ * discriminated-union type (emitted by the tph-discriminator template) owns the
44
+ * bare `<Base>` name, so the raw single-table row type is emitted as `<Base>Row`
45
+ * to avoid a duplicate `export type <Base>`. Insert/Update keep their names
46
+ * (no collision); they describe the physical TPH table row shape.
47
+ */
48
+ export function renderInferredTypes(entity: MetaObject, tphBase = false, ctx?: RenderContext): Code {
49
+ // The inferred Row/Insert types reference the Drizzle table var, so they must
50
+ // resolve to the SAME (possibly overridden) collection name the schema emits.
51
+ // ctx is optional for bare unit-test calls — those fall back to the default
52
+ // always-pluralize spelling.
53
+ const varName = ctx ? ctx.collectionName(entity.name) : variableNameFromEntity(entity.name);
39
54
  const selectSym = imp("InferSelectModel@drizzle-orm");
40
55
  const insertSym = imp("InferInsertModel@drizzle-orm");
41
56
  const docs = renderDocsFor(entity);
42
57
  const docsPrefix = docs ? `${docs}\n` : "";
58
+ const rowName = tphBase ? `${entity.name}Row` : entity.name;
43
59
  return code`
44
- ${docsPrefix}export type ${entity.name} = ${selectSym}<typeof ${varName}>;
60
+ ${docsPrefix}export type ${rowName} = ${selectSym}<typeof ${varName}>;
45
61
  export type ${entity.name}Insert = ${insertSym}<typeof ${varName}>;
46
62
  export type ${entity.name}Update = Partial<${entity.name}Insert>;
47
63
  `;
@@ -71,12 +87,23 @@ export function enumUnionString(values: string[]): string {
71
87
  }
72
88
 
73
89
  /**
74
- * Emit one `export type <Name> = "A" | "B";` line per field.enum field on the entity.
75
- * - If the field extends an abstract field.enum (super), use the super field's PascalCase name.
76
- * - Otherwise use `<Entity><FieldPascal>` for inline enums.
77
- * Returns null if the entity has no enum fields.
90
+ * Emit the enum type-alias section for an entity file. Three cases per field:
91
+ *
92
+ * inline enum (members declared directly on the field; no root-abstract super)
93
+ * `export type <Entity><Field> = "A" | "B";` UNCHANGED (byte-identical).
94
+ * • shared materialized enum (extends a NON-@provided root-level abstract
95
+ * field.enum) → re-export the materialized type from the shared `./enums`
96
+ * module (`export { type E } from "./enums"`) instead of redeclaring it. The
97
+ * type is materialized ONCE in enums.ts (FR-019).
98
+ * • provided enum (extends a @provided root-level abstract field.enum) →
99
+ * re-export the type from the configured external module
100
+ * (`export { type E } from "<providedEnumModule>"`); metaobjects emits no
101
+ * declaration for it. A missing config is a codegen-time error.
102
+ *
103
+ * `ctx` is required to compute the shared/provided import specifiers. Returns
104
+ * null when the entity has no enum-alias lines to emit.
78
105
  */
79
- export function renderEnumTypeAliases(entity: MetaObject): Code | null {
106
+ export function renderEnumTypeAliases(entity: MetaObject, ctx?: RenderContext): Code | null {
80
107
  // De-duplicate by type-alias name — multiple fields can extend the same abstract enum.
81
108
  const seen = new Set<string>();
82
109
  const lines: string[] = [];
@@ -91,7 +118,21 @@ export function renderEnumTypeAliases(entity: MetaObject): Code | null {
91
118
  if (seen.has(typeName)) continue;
92
119
  seen.add(typeName);
93
120
 
94
- lines.push(`export type ${typeName} = ${enumUnionString(values)};`);
121
+ // Without a RenderContext (bare unit-test calls) the shared/provided import
122
+ // specifiers can't be computed — fall back to inline emission. Real runs
123
+ // always pass ctx (entity-file template), so shared materialization applies.
124
+ const shared = ctx !== undefined ? sharedEnumForField(field) : undefined;
125
+ if (shared === undefined) {
126
+ // Inline enum — emit the literal union exactly as before.
127
+ lines.push(`export type ${typeName} = ${enumUnionString(values)};`);
128
+ continue;
129
+ }
130
+ // Shared / provided enum — re-export from the materialized module or the
131
+ // configured external module; never redeclare the union here.
132
+ const spec = shared.provided
133
+ ? providedEnumImportSpecifier(ctx!, shared.name)
134
+ : sharedEnumImportSpecifier(ctx!, entity.package);
135
+ lines.push(`export { type ${shared.name} } from ${JSON.stringify(spec)};`);
95
136
  }
96
137
 
97
138
  return lines.length > 0 ? code`${lines.join("\n")}` : null;
@@ -103,11 +144,8 @@ export function renderEnumTypeAliases(entity: MetaObject): Code | null {
103
144
 
104
145
  const SCALAR_TS_BY_SUBTYPE: Record<string, string> = {
105
146
  [FIELD_SUBTYPE_STRING]: "string",
106
- [FIELD_SUBTYPE_CLASS]: "string",
107
147
  [FIELD_SUBTYPE_UUID]: "string",
108
148
  [FIELD_SUBTYPE_INT]: "number",
109
- [FIELD_SUBTYPE_SHORT]: "number",
110
- [FIELD_SUBTYPE_BYTE]: "number",
111
149
  [FIELD_SUBTYPE_LONG]: "number",
112
150
  [FIELD_SUBTYPE_DOUBLE]: "number",
113
151
  [FIELD_SUBTYPE_FLOAT]: "number",
@@ -122,19 +160,68 @@ const SCALAR_TS_BY_SUBTYPE: Record<string, string> = {
122
160
  [FIELD_SUBTYPE_TIMESTAMP]: "string",
123
161
  };
124
162
 
163
+ /**
164
+ * The PLAIN-STRING TS type expression for a field — the SINGLE source of truth
165
+ * for "what TS type does the codegen give this field". `valueObjectFieldType`
166
+ * (which returns a `Code` so cross-module `field.object` refs hoist via
167
+ * `imp(...)`) makes the SAME per-branch decisions; this string form exists for
168
+ * consumers (the api-docs field-shape builder) that need the type name as text,
169
+ * not a hoisting `Code`. The branch logic MUST stay in lock-step with
170
+ * `valueObjectFieldType` below.
171
+ *
172
+ * • field.object → the referenced object's bare (package-stripped) name, `[]`
173
+ * when an array; `unknown` / `unknown[]` when the @objectRef is missing.
174
+ * • field.enum → the same enum-union alias `enumUnionAliasName` emits
175
+ * (`<Owner><Field>` or the abstract super's PascalCase), `string` fallback.
176
+ * • scalar → SCALAR_TS_BY_SUBTYPE (else `unknown`), `[]` when an array.
177
+ */
178
+ export function fieldTsTypeString(ownerName: string, field: MetaField): string {
179
+ if (field.subType === FIELD_SUBTYPE_OBJECT) {
180
+ const ref = field.attr(FIELD_ATTR_OBJECT_REF);
181
+ if (typeof ref === "string" && ref.length > 0) {
182
+ const base = stripPackage(ref);
183
+ return field.isArray ? `${base}[]` : base;
184
+ }
185
+ return field.isArray ? "unknown[]" : "unknown";
186
+ }
187
+ if (field.subType === FIELD_SUBTYPE_ENUM) {
188
+ const values = enumValues(field);
189
+ if (values !== undefined) {
190
+ // The emitted TS type is an enum-union ALIAS (`<Owner><Field>`), but its
191
+ // definition IS this literal union — inline it so the documented shape is
192
+ // self-contained (an agent sees the exact allowed values, not an opaque
193
+ // alias name). Array enums wrap the parenthesized union: `(A | B)[]`.
194
+ const union = enumUnionString(values);
195
+ return field.isArray ? `(${union})[]` : union;
196
+ }
197
+ return field.isArray ? "string[]" : "string";
198
+ }
199
+ const scalar = SCALAR_TS_BY_SUBTYPE[field.subType] ?? "unknown";
200
+ return field.isArray ? `${scalar}[]` : scalar;
201
+ }
202
+
125
203
  /**
126
204
  * One-line TS type expression for a field on a value-only object.
127
205
  * Returns a `Code` so cross-module `field.object` refs can be hoisted via
128
206
  * ts-poet `imp(...)` — matching how the Zod emitter hoists `<Ref>InsertSchema`.
129
207
  */
130
- function valueObjectFieldType(entity: MetaObject, field: MetaField): Code {
208
+ function valueObjectFieldType(entity: MetaObject, field: MetaField, ctx?: RenderContext): Code {
131
209
  // field.object: import the referenced TS interface from its sibling module
132
210
  // so ts-poet hoists the import. Mirrors zod-validators.ts's `<Ref>InsertSchema`
133
211
  // import strategy, just for the type alias instead of the schema constant.
134
212
  if (field.subType === FIELD_SUBTYPE_OBJECT) {
135
- const ref = field.ownAttr(FIELD_ATTR_OBJECT_REF);
213
+ const ref = field.attr(FIELD_ATTR_OBJECT_REF);
136
214
  if (typeof ref === "string" && ref.length > 0) {
137
- const refImp = imp(`${ref}@./${ref}.js`);
215
+ // @objectRef may be authored fully-qualified (acme::sales::Brief) or bare; the
216
+ // referenced interface is named by the BARE short name. The import MODULE is
217
+ // resolved through the shared layout/package/extStyle-aware helper (the SAME
218
+ // one the Zod schema + Drizzle .$type<> use) so all three agree. Without a
219
+ // ctx (bare unit-test calls) fall back to the flat same-dir specifier.
220
+ const base = stripPackage(ref);
221
+ const moduleSpec = ctx
222
+ ? valueObjectModuleSpecifier(base, ctx.packageOf, entity.package, ctx.outputLayout, ctx.extStyle)
223
+ : `./${base}.js`;
224
+ const refImp = imp(`${base}@${moduleSpec}`);
138
225
  return field.isArray ? code`${refImp}[]` : code`${refImp}`;
139
226
  }
140
227
  return field.isArray ? code`unknown[]` : code`unknown`;
@@ -145,6 +232,20 @@ function valueObjectFieldType(entity: MetaObject, field: MetaField): Code {
145
232
  const values = enumValues(field);
146
233
  if (values !== undefined) {
147
234
  const alias = enumUnionAliasName(entity.name, field);
235
+ // FR-019: a shared/provided enum's type lives in another module (./enums or
236
+ // the provided module). Use imp() so ts-poet hoists `import { type E }` —
237
+ // the local interface can then reference E. Inline enums reference the
238
+ // locally-declared `<Entity><Field>` alias as before.
239
+ if (ctx !== undefined) {
240
+ const shared = sharedEnumForField(field);
241
+ if (shared !== undefined) {
242
+ const spec = shared.provided
243
+ ? providedEnumImportSpecifier(ctx, shared.name)
244
+ : sharedEnumImportSpecifier(ctx, entity.package);
245
+ const sym = imp(`${shared.name}@${spec}`);
246
+ return field.isArray ? code`${sym}[]` : code`${sym}`;
247
+ }
248
+ }
148
249
  return field.isArray ? code`${alias}[]` : code`${alias}`;
149
250
  }
150
251
  return field.isArray ? code`string[]` : code`string`;
@@ -162,15 +263,15 @@ function valueObjectFieldType(entity: MetaObject, field: MetaField): Code {
162
263
  * trip through Drizzle nullable columns, so the null-bridge is unnecessary
163
264
  * here — and forces consumers into a residual cast at the call site.
164
265
  */
165
- export function renderValueObjectInterface(entity: MetaObject): Code {
266
+ export function renderValueObjectInterface(entity: MetaObject, ctx?: RenderContext): Code {
166
267
  const docs = renderDocsFor(entity);
167
268
  const docsPrefix = docs ? `${docs}\n` : "";
168
269
 
169
270
  const lines: Code[] = [];
170
271
  for (const field of entity.fields()) {
171
- const required = field.ownAttr(FIELD_ATTR_REQUIRED) === true;
272
+ const required = field.attr(FIELD_ATTR_REQUIRED) === true;
172
273
  const optional = required ? "" : "?";
173
- const tsType = valueObjectFieldType(entity, field);
274
+ const tsType = valueObjectFieldType(entity, field, ctx);
174
275
  lines.push(code` ${field.name}${optional}: ${tsType};`);
175
276
  }
176
277