@metaobjectsdev/codegen-ts 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 (198) hide show
  1. package/LICENSE +189 -0
  2. package/README.md +101 -0
  3. package/dist/column-mapper.d.ts +38 -0
  4. package/dist/column-mapper.d.ts.map +1 -0
  5. package/dist/column-mapper.js +205 -0
  6. package/dist/column-mapper.js.map +1 -0
  7. package/dist/constants.d.ts +7 -0
  8. package/dist/constants.d.ts.map +1 -0
  9. package/dist/constants.js +8 -0
  10. package/dist/constants.js.map +1 -0
  11. package/dist/errors.d.ts +7 -0
  12. package/dist/errors.d.ts.map +1 -0
  13. package/dist/errors.js +11 -0
  14. package/dist/errors.js.map +1 -0
  15. package/dist/format.d.ts +2 -0
  16. package/dist/format.d.ts.map +1 -0
  17. package/dist/format.js +47 -0
  18. package/dist/format.js.map +1 -0
  19. package/dist/generator.d.ts +44 -0
  20. package/dist/generator.d.ts.map +1 -0
  21. package/dist/generator.js +17 -0
  22. package/dist/generator.js.map +1 -0
  23. package/dist/generators/barrel.d.ts +6 -0
  24. package/dist/generators/barrel.d.ts.map +1 -0
  25. package/dist/generators/barrel.js +17 -0
  26. package/dist/generators/barrel.js.map +1 -0
  27. package/dist/generators/entity-file.d.ts +8 -0
  28. package/dist/generators/entity-file.d.ts.map +1 -0
  29. package/dist/generators/entity-file.js +27 -0
  30. package/dist/generators/entity-file.js.map +1 -0
  31. package/dist/generators/index.d.ts +5 -0
  32. package/dist/generators/index.d.ts.map +1 -0
  33. package/dist/generators/index.js +5 -0
  34. package/dist/generators/index.js.map +1 -0
  35. package/dist/generators/queries-file.d.ts +8 -0
  36. package/dist/generators/queries-file.d.ts.map +1 -0
  37. package/dist/generators/queries-file.js +26 -0
  38. package/dist/generators/queries-file.js.map +1 -0
  39. package/dist/generators/routes-file.d.ts +12 -0
  40. package/dist/generators/routes-file.d.ts.map +1 -0
  41. package/dist/generators/routes-file.js +30 -0
  42. package/dist/generators/routes-file.js.map +1 -0
  43. package/dist/import-path.d.ts +41 -0
  44. package/dist/import-path.d.ts.map +1 -0
  45. package/dist/import-path.js +95 -0
  46. package/dist/import-path.js.map +1 -0
  47. package/dist/index.d.ts +29 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +21 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/metaobjects-config.d.ts +56 -0
  52. package/dist/metaobjects-config.d.ts.map +1 -0
  53. package/dist/metaobjects-config.js +42 -0
  54. package/dist/metaobjects-config.js.map +1 -0
  55. package/dist/naming.d.ts +29 -0
  56. package/dist/naming.d.ts.map +1 -0
  57. package/dist/naming.js +67 -0
  58. package/dist/naming.js.map +1 -0
  59. package/dist/overwrite-policy.d.ts +8 -0
  60. package/dist/overwrite-policy.d.ts.map +1 -0
  61. package/dist/overwrite-policy.js +23 -0
  62. package/dist/overwrite-policy.js.map +1 -0
  63. package/dist/pk-resolver.d.ts +18 -0
  64. package/dist/pk-resolver.d.ts.map +1 -0
  65. package/dist/pk-resolver.js +36 -0
  66. package/dist/pk-resolver.js.map +1 -0
  67. package/dist/projection/extract-view-spec.d.ts +18 -0
  68. package/dist/projection/extract-view-spec.d.ts.map +1 -0
  69. package/dist/projection/extract-view-spec.js +272 -0
  70. package/dist/projection/extract-view-spec.js.map +1 -0
  71. package/dist/projection/index.d.ts +5 -0
  72. package/dist/projection/index.d.ts.map +1 -0
  73. package/dist/projection/index.js +5 -0
  74. package/dist/projection/index.js.map +1 -0
  75. package/dist/projection/projection-detector.d.ts +4 -0
  76. package/dist/projection/projection-detector.d.ts.map +1 -0
  77. package/dist/projection/projection-detector.js +13 -0
  78. package/dist/projection/projection-detector.js.map +1 -0
  79. package/dist/projection/view-ddl-emit.d.ts +10 -0
  80. package/dist/projection/view-ddl-emit.d.ts.map +1 -0
  81. package/dist/projection/view-ddl-emit.js +47 -0
  82. package/dist/projection/view-ddl-emit.js.map +1 -0
  83. package/dist/projection/view-spec.d.ts +56 -0
  84. package/dist/projection/view-spec.d.ts.map +1 -0
  85. package/dist/projection/view-spec.js +2 -0
  86. package/dist/projection/view-spec.js.map +1 -0
  87. package/dist/relation-resolver.d.ts +21 -0
  88. package/dist/relation-resolver.d.ts.map +1 -0
  89. package/dist/relation-resolver.js +62 -0
  90. package/dist/relation-resolver.js.map +1 -0
  91. package/dist/render-context.d.ts +65 -0
  92. package/dist/render-context.d.ts.map +1 -0
  93. package/dist/render-context.js +28 -0
  94. package/dist/render-context.js.map +1 -0
  95. package/dist/runner.d.ts +17 -0
  96. package/dist/runner.d.ts.map +1 -0
  97. package/dist/runner.js +135 -0
  98. package/dist/runner.js.map +1 -0
  99. package/dist/templates/barrel.d.ts +8 -0
  100. package/dist/templates/barrel.d.ts.map +1 -0
  101. package/dist/templates/barrel.js +12 -0
  102. package/dist/templates/barrel.js.map +1 -0
  103. package/dist/templates/drizzle-schema.d.ts +13 -0
  104. package/dist/templates/drizzle-schema.d.ts.map +1 -0
  105. package/dist/templates/drizzle-schema.js +251 -0
  106. package/dist/templates/drizzle-schema.js.map +1 -0
  107. package/dist/templates/entity-constants.d.ts +4 -0
  108. package/dist/templates/entity-constants.d.ts.map +1 -0
  109. package/dist/templates/entity-constants.js +215 -0
  110. package/dist/templates/entity-constants.js.map +1 -0
  111. package/dist/templates/entity-file.d.ts +4 -0
  112. package/dist/templates/entity-file.d.ts.map +1 -0
  113. package/dist/templates/entity-file.js +45 -0
  114. package/dist/templates/entity-file.js.map +1 -0
  115. package/dist/templates/field-meta.d.ts +24 -0
  116. package/dist/templates/field-meta.d.ts.map +1 -0
  117. package/dist/templates/field-meta.js +117 -0
  118. package/dist/templates/field-meta.js.map +1 -0
  119. package/dist/templates/filter-allowlist.d.ts +5 -0
  120. package/dist/templates/filter-allowlist.d.ts.map +1 -0
  121. package/dist/templates/filter-allowlist.js +86 -0
  122. package/dist/templates/filter-allowlist.js.map +1 -0
  123. package/dist/templates/filter-shared.d.ts +15 -0
  124. package/dist/templates/filter-shared.d.ts.map +1 -0
  125. package/dist/templates/filter-shared.js +30 -0
  126. package/dist/templates/filter-shared.js.map +1 -0
  127. package/dist/templates/filter-type.d.ts +4 -0
  128. package/dist/templates/filter-type.d.ts.map +1 -0
  129. package/dist/templates/filter-type.js +78 -0
  130. package/dist/templates/filter-type.js.map +1 -0
  131. package/dist/templates/inferred-types.d.ts +4 -0
  132. package/dist/templates/inferred-types.d.ts.map +1 -0
  133. package/dist/templates/inferred-types.js +14 -0
  134. package/dist/templates/inferred-types.js.map +1 -0
  135. package/dist/templates/projection-decl.d.ts +21 -0
  136. package/dist/templates/projection-decl.d.ts.map +1 -0
  137. package/dist/templates/projection-decl.js +116 -0
  138. package/dist/templates/projection-decl.js.map +1 -0
  139. package/dist/templates/queries-file.d.ts +4 -0
  140. package/dist/templates/queries-file.d.ts.map +1 -0
  141. package/dist/templates/queries-file.js +39 -0
  142. package/dist/templates/queries-file.js.map +1 -0
  143. package/dist/templates/queries.d.ts +9 -0
  144. package/dist/templates/queries.d.ts.map +1 -0
  145. package/dist/templates/queries.js +115 -0
  146. package/dist/templates/queries.js.map +1 -0
  147. package/dist/templates/relations-block.d.ts +9 -0
  148. package/dist/templates/relations-block.d.ts.map +1 -0
  149. package/dist/templates/relations-block.js +45 -0
  150. package/dist/templates/relations-block.js.map +1 -0
  151. package/dist/templates/routes-file.d.ts +4 -0
  152. package/dist/templates/routes-file.d.ts.map +1 -0
  153. package/dist/templates/routes-file.js +158 -0
  154. package/dist/templates/routes-file.js.map +1 -0
  155. package/dist/templates/zod-validators.d.ts +4 -0
  156. package/dist/templates/zod-validators.d.ts.map +1 -0
  157. package/dist/templates/zod-validators.js +129 -0
  158. package/dist/templates/zod-validators.js.map +1 -0
  159. package/package.json +59 -0
  160. package/src/column-mapper.ts +266 -0
  161. package/src/constants.ts +10 -0
  162. package/src/errors.ts +10 -0
  163. package/src/format.ts +50 -0
  164. package/src/generator.ts +73 -0
  165. package/src/generators/barrel.ts +28 -0
  166. package/src/generators/entity-file.ts +33 -0
  167. package/src/generators/index.ts +4 -0
  168. package/src/generators/queries-file.ts +32 -0
  169. package/src/generators/routes-file.ts +36 -0
  170. package/src/import-path.ts +153 -0
  171. package/src/index.ts +45 -0
  172. package/src/metaobjects-config.ts +95 -0
  173. package/src/naming.ts +84 -0
  174. package/src/overwrite-policy.ts +39 -0
  175. package/src/pk-resolver.ts +47 -0
  176. package/src/projection/extract-view-spec.ts +372 -0
  177. package/src/projection/index.ts +4 -0
  178. package/src/projection/projection-detector.ts +26 -0
  179. package/src/projection/view-ddl-emit.ts +66 -0
  180. package/src/projection/view-spec.ts +62 -0
  181. package/src/relation-resolver.ts +87 -0
  182. package/src/render-context.ts +93 -0
  183. package/src/runner.ts +178 -0
  184. package/src/templates/barrel.ts +23 -0
  185. package/src/templates/drizzle-schema.ts +286 -0
  186. package/src/templates/entity-constants.ts +248 -0
  187. package/src/templates/entity-file.ts +51 -0
  188. package/src/templates/field-meta.ts +150 -0
  189. package/src/templates/filter-allowlist.ts +104 -0
  190. package/src/templates/filter-shared.ts +30 -0
  191. package/src/templates/filter-type.ts +93 -0
  192. package/src/templates/inferred-types.ts +16 -0
  193. package/src/templates/projection-decl.ts +146 -0
  194. package/src/templates/queries-file.ts +56 -0
  195. package/src/templates/queries.ts +132 -0
  196. package/src/templates/relations-block.ts +65 -0
  197. package/src/templates/routes-file.ts +179 -0
  198. package/src/templates/zod-validators.ts +140 -0
@@ -0,0 +1,47 @@
1
+ import { toSnakeCase } from "../naming.js";
2
+ function renderColumn(c) {
3
+ if (c.kind === "passthrough") {
4
+ return `${c.sourceAlias}.${c.sourceColumn} AS ${c.dbColAlias}`;
5
+ }
6
+ // aggregate — use DISTINCT for count() over joined PKs to avoid join inflation.
7
+ if (c.agg === "count") {
8
+ return `COUNT(DISTINCT ${c.sourceAlias}.${c.sourceColumn}) AS ${c.dbColAlias}`;
9
+ }
10
+ return `${c.agg.toUpperCase()}(${c.sourceAlias}.${c.sourceColumn}) AS ${c.dbColAlias}`;
11
+ }
12
+ function renderJoin(node, parentAlias, options) {
13
+ const table = options.joinTables[node.targetEntity];
14
+ if (!table) {
15
+ throw new Error(`view-ddl-emit: no table name registered for joined entity "${node.targetEntity}".`);
16
+ }
17
+ // JoinNode stores raw camelCase field names from metadata (e.g. "programId").
18
+ // Emit always applies snake_case — view DDL is SQL-side and always snake_case.
19
+ const fkCol = toSnakeCase(node.fkField);
20
+ const pkCol = toSnakeCase(node.pkField);
21
+ const childAlias = node.alias;
22
+ // referenceHolder = "source" → FK on parent (source): child.pk = parent.fk (belongs-to)
23
+ // referenceHolder = "target" → FK on child (target): child.fk = parent.pk (has-many)
24
+ const onClause = node.referenceHolder === "source"
25
+ ? `${childAlias}.${pkCol} = ${parentAlias}.${fkCol}`
26
+ : `${childAlias}.${fkCol} = ${parentAlias}.${pkCol}`;
27
+ let sql = ` LEFT OUTER JOIN ${table} ${childAlias} ON ${onClause}`;
28
+ for (const childJoin of node.children) {
29
+ sql += "\n" + renderJoin(childJoin, childAlias, options);
30
+ }
31
+ return sql;
32
+ }
33
+ export function emitViewDdl(spec, options) {
34
+ const cols = spec.selectSpec.columns
35
+ .map((c) => " " + renderColumn(c))
36
+ .join(",\n");
37
+ const fromClause = ` FROM ${options.baseTableName} ${spec.joinTree.baseAlias}`;
38
+ const joinsClause = spec.joinTree.joins
39
+ .map((j) => renderJoin(j, spec.joinTree.baseAlias, options))
40
+ .join("\n");
41
+ const groupByClause = spec.groupBy.length > 0 ? `\n GROUP BY ${spec.groupBy.join(", ")}` : "";
42
+ return `CREATE VIEW ${spec.viewName} AS
43
+ SELECT
44
+ ${cols}
45
+ ${fromClause}${joinsClause ? "\n" + joinsClause : ""}${groupByClause};`;
46
+ }
47
+ //# sourceMappingURL=view-ddl-emit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-ddl-emit.js","sourceRoot":"","sources":["../../src/projection/view-ddl-emit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import type { AggregateFunction } from "@metaobjectsdev/metadata";
2
+ /** One node in the JOIN tree. `alias` is an auto-generated unique short alias. */
3
+ export interface JoinNode {
4
+ /** Relationship name on the parent object (e.g., "weeks"). */
5
+ readonly relationship: string;
6
+ /** Entity name this join lands on (e.g., "Week"). */
7
+ readonly targetEntity: string;
8
+ /** Auto-assigned SQL alias for this join (e.g., "w", "w0"). */
9
+ readonly alias: string;
10
+ /** Cardinality of the relationship being traversed. */
11
+ readonly cardinality: "one" | "many";
12
+ /** FK field name (lives on whichever side `referenceHolder` indicates). */
13
+ readonly fkField: string;
14
+ /** PK field name on the side that does NOT hold the FK. */
15
+ readonly pkField: string;
16
+ /** Which side of this hop physically holds the FK: the parent (source) or the child (target). */
17
+ readonly referenceHolder: "source" | "target";
18
+ /** Child joins. */
19
+ readonly children: readonly JoinNode[];
20
+ }
21
+ /** Tree of JOINs rooted at the projection's base entity. */
22
+ export interface JoinTree {
23
+ /** Base entity name (e.g., "Program"). */
24
+ readonly baseEntity: string;
25
+ /** SQL alias for the base entity (typically "p", "p0"). */
26
+ readonly baseAlias: string;
27
+ /** Joined entities (could be empty for a flat projection). */
28
+ readonly joins: readonly JoinNode[];
29
+ }
30
+ /** One column of the SELECT list. */
31
+ export type SelectColumn = {
32
+ readonly kind: "passthrough";
33
+ readonly fieldName: string;
34
+ readonly dbColAlias: string;
35
+ readonly sourceAlias: string;
36
+ readonly sourceColumn: string;
37
+ } | {
38
+ readonly kind: "aggregate";
39
+ readonly fieldName: string;
40
+ readonly dbColAlias: string;
41
+ readonly agg: AggregateFunction;
42
+ readonly sourceAlias: string;
43
+ readonly sourceColumn: string;
44
+ };
45
+ export interface SelectSpec {
46
+ readonly columns: readonly SelectColumn[];
47
+ }
48
+ /** Top-level view specification consumed by view-ddl-emit + Drizzle declaration. */
49
+ export interface ViewSpec {
50
+ readonly viewName: string;
51
+ readonly joinTree: JoinTree;
52
+ readonly selectSpec: SelectSpec;
53
+ /** non-aggregate column SQL fragments to put in GROUP BY (empty if no aggregates). */
54
+ readonly groupBy: readonly string[];
55
+ }
56
+ //# sourceMappingURL=view-spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-spec.d.ts","sourceRoot":"","sources":["../../src/projection/view-spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,kFAAkF;AAClF,MAAM,WAAW,QAAQ;IACvB,8DAA8D;IAC9D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,qDAAqD;IACrD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,uDAAuD;IACvD,QAAQ,CAAC,WAAW,EAAE,KAAK,GAAG,MAAM,CAAC;IACrC,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,iGAAiG;IACjG,QAAQ,CAAC,eAAe,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC9C,mBAAmB;IACnB,QAAQ,CAAC,QAAQ,EAAE,SAAS,QAAQ,EAAE,CAAC;CACxC;AAED,4DAA4D;AAC5D,MAAM,WAAW,QAAQ;IACvB,0CAA0C;IAC1C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,2DAA2D;IAC3D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAC;CACrC;AAED,qCAAqC;AACrC,MAAM,MAAM,YAAY,GACpB;IACE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,iBAAiB,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEN,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;CAC3C;AAED,oFAAoF;AACpF,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,sFAAsF;IACtF,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=view-spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-spec.js","sourceRoot":"","sources":["../../src/projection/view-spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,21 @@
1
+ import type { MetaRoot } from "@metaobjectsdev/metadata";
2
+ export interface RelationEntry {
3
+ /** Name of the relationship (e.g., "author") */
4
+ name: string;
5
+ /** Cardinality: 'one' | 'many' */
6
+ cardinality: "one" | "many";
7
+ /** The other entity's name (e.g., "User") */
8
+ targetEntity: string;
9
+ /** For cardinality 'one': the field on THIS entity that holds the FK (e.g., "authorId") */
10
+ fkField?: string;
11
+ /** For cardinality 'one': the target entity's PK field (e.g., "id") */
12
+ targetPkField?: string;
13
+ }
14
+ /** Map from entity name → list of relations for that entity's relations() block */
15
+ export type RelationMap = Map<string, RelationEntry[]>;
16
+ /**
17
+ * Walk all entities, collect relationship children, and also register inverse
18
+ * many() sides on the target entity.
19
+ */
20
+ export declare function buildRelationMap(root: MetaRoot): RelationMap;
21
+ //# sourceMappingURL=relation-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relation-resolver.d.ts","sourceRoot":"","sources":["../src/relation-resolver.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAUzD,MAAM,WAAW,aAAa;IAC5B,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,WAAW,EAAE,KAAK,GAAG,MAAM,CAAC;IAC5B,6CAA6C;IAC7C,YAAY,EAAE,MAAM,CAAC;IACrB,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,mFAAmF;AACnF,MAAM,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC;AAEvD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,GAAG,WAAW,CAiD5D"}
@@ -0,0 +1,62 @@
1
+ // Relation resolver — pre-pass that builds the inverse-side map for relations() emission.
2
+ // For each entity, we need to know:
3
+ // - Which outgoing belongs-to relationships it declares (one-side, reference on this entity)
4
+ // - Which incoming relationships point to it (many-side, reference on the other entity)
5
+ //
6
+ // Reads identity.reference declarations to determine the physical reference side.
7
+ import { RELATIONSHIP_ATTR_CARDINALITY, RELATIONSHIP_ATTR_OBJECT_REF, CARDINALITY_ONE, stripPackage, } from "@metaobjectsdev/metadata";
8
+ import { variableNameFromEntity } from "./naming.js";
9
+ import { isProjection } from "./projection/projection-detector.js";
10
+ /**
11
+ * Walk all entities, collect relationship children, and also register inverse
12
+ * many() sides on the target entity.
13
+ */
14
+ export function buildRelationMap(root) {
15
+ const result = new Map();
16
+ const ensure = (name) => {
17
+ if (!result.has(name))
18
+ result.set(name, []);
19
+ return result.get(name);
20
+ };
21
+ for (const obj of root.objects()) {
22
+ // Projections (source.dbView) are view-backed; they never emit a relations()
23
+ // block, and their inherited belongs-to relationships would otherwise register
24
+ // a spurious inverse-many on the target entity.
25
+ if (isProjection(obj))
26
+ continue;
27
+ for (const child of obj.relationships()) {
28
+ const cardinality = child.ownAttr(RELATIONSHIP_ATTR_CARDINALITY);
29
+ if (cardinality !== CARDINALITY_ONE)
30
+ continue;
31
+ const targetEntityRaw = child.ownAttr(RELATIONSHIP_ATTR_OBJECT_REF);
32
+ if (!targetEntityRaw)
33
+ continue;
34
+ const targetEntity = stripPackage(targetEntityRaw);
35
+ // Find an identity.reference on `obj` whose @references targets this relationship's target.
36
+ // Compare against package-stripped names since both relationship @objectRef and
37
+ // identity.reference @references may carry package-qualified entity names.
38
+ const refs = obj.referenceIdentities();
39
+ const matching = refs.find((r) => stripPackage(r.targetEntity ?? "") === targetEntity);
40
+ if (!matching)
41
+ continue;
42
+ const fkFields = matching.fields;
43
+ if (fkFields.length === 0)
44
+ continue;
45
+ const fkField = fkFields[0];
46
+ ensure(obj.name).push({
47
+ name: child.name,
48
+ cardinality: "one",
49
+ targetEntity,
50
+ fkField,
51
+ targetPkField: "id",
52
+ });
53
+ ensure(targetEntity).push({
54
+ name: variableNameFromEntity(obj.name),
55
+ cardinality: "many",
56
+ targetEntity: obj.name,
57
+ });
58
+ }
59
+ }
60
+ return result;
61
+ }
62
+ //# sourceMappingURL=relation-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relation-resolver.js","sourceRoot":"","sources":["../src/relation-resolver.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,oCAAoC;AACpC,+FAA+F;AAC/F,0FAA0F;AAC1F,EAAE;AACF,kFAAkF;AAGlF,OAAO,EACL,6BAA6B,EAC7B,4BAA4B,EAC5B,eAAe,EACf,YAAY,GACb,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AAkBnE;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAc;IAC7C,MAAM,MAAM,GAAgB,IAAI,GAAG,EAAE,CAAC;IAEtC,MAAM,MAAM,GAAG,CAAC,IAAY,EAAmB,EAAE;QAC/C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC5C,OAAO,MAAM,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC;IAC3B,CAAC,CAAC;IAEF,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;QACjC,6EAA6E;QAC7E,+EAA+E;QAC/E,gDAAgD;QAChD,IAAI,YAAY,CAAC,GAAG,CAAC;YAAE,SAAS;QAEhC,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,aAAa,EAAE,EAAE,CAAC;YACxC,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,6BAA6B,CAAuB,CAAC;YACvF,IAAI,WAAW,KAAK,eAAe;gBAAE,SAAS;YAE9C,MAAM,eAAe,GAAG,KAAK,CAAC,OAAO,CAAC,4BAA4B,CAAuB,CAAC;YAC1F,IAAI,CAAC,eAAe;gBAAE,SAAS;YAC/B,MAAM,YAAY,GAAG,YAAY,CAAC,eAAe,CAAC,CAAC;YAEnD,4FAA4F;YAC5F,gFAAgF;YAChF,2EAA2E;YAC3E,MAAM,IAAI,GAAG,GAAG,CAAC,mBAAmB,EAAE,CAAC;YACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,IAAI,EAAE,CAAC,KAAK,YAAY,CAAC,CAAC;YACvF,IAAI,CAAC,QAAQ;gBAAE,SAAS;YAExB,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC;YACjC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YACpC,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAE,CAAC;YAE7B,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC;gBACpB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,WAAW,EAAE,KAAK;gBAClB,YAAY;gBACZ,OAAO;gBACP,aAAa,EAAE,IAAI;aACpB,CAAC,CAAC;YACH,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC;gBACxB,IAAI,EAAE,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC;gBACtC,WAAW,EAAE,MAAM;gBACnB,YAAY,EAAE,GAAG,CAAC,IAAI;aACvB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,65 @@
1
+ import type { MetaRoot } from "@metaobjectsdev/metadata";
2
+ import type { Dialect } from "./column-mapper.js";
3
+ import type { PkInfo } from "./pk-resolver.js";
4
+ import type { RelationMap } from "./relation-resolver.js";
5
+ import type { ColumnNamingStrategy } from "./metaobjects-config.js";
6
+ import type { OutputLayout, ResolvedTarget } from "./import-path.js";
7
+ /**
8
+ * How to format cross-entity import specifiers in generated files.
9
+ * - "none" → emit `"./Foo"`. **Default.** Works for moduleResolution
10
+ * "bundler"/"node", tsx, drizzle-kit's TS loader, Vite, esbuild,
11
+ * and almost every modern TS toolchain.
12
+ * - "js" → emit `"./Foo.js"`. Required for Node ESM strict + TS NodeNext.
13
+ * Opt in via `--ext-style js` or `codegen.extStyle = "js"`.
14
+ */
15
+ export type ExtStyle = "js" | "none";
16
+ export interface RenderContext {
17
+ dialect: Dialect;
18
+ loadedRoot: MetaRoot;
19
+ outDir: string;
20
+ /**
21
+ * Import path for { db } in generated .queries.ts files.
22
+ * E.g. '~/server/db'. Set via forge.config.ts codegen.dbImport.
23
+ */
24
+ dbImport: string;
25
+ /**
26
+ * Import path for { om } in generated .routes.ts files.
27
+ * E.g. '../index' or '@your-pkg/database'. The module is expected to export
28
+ * an `om()` function returning a Promise<ObjectManager>.
29
+ * Defaults to '../index' (one level up from outDir).
30
+ */
31
+ omImport: string;
32
+ /** Cross-entity import-specifier style. Defaults to "js" (Node-ESM-safe). */
33
+ extStyle: ExtStyle;
34
+ /** Column naming strategy: how field names map to DB column names. Defaults to "snake_case". */
35
+ columnNamingStrategy: ColumnNamingStrategy;
36
+ /** Path prefix applied to generated route registrations + hook fetch URLs. Defaults to "". */
37
+ apiPrefix: string;
38
+ /** Output layout mode: "flat" (default) — all files in outDir; "package" — sub-paths from entity metadata package. */
39
+ outputLayout: OutputLayout;
40
+ /** The target THIS generator emits to (drives path layout + same-target imports). */
41
+ selfTarget: ResolvedTarget;
42
+ /** Where entity files live (drives cross-target entity imports). */
43
+ entityModuleTarget: ResolvedTarget;
44
+ pkMap: Map<string, PkInfo>;
45
+ /** Pre-pass relation map for FK + relations() block emission. */
46
+ relationMap: RelationMap;
47
+ /** Entity name → its metadata package (undefined if the entity has no package). Built once per run. */
48
+ packageOf: Map<string, string | undefined>;
49
+ }
50
+ /** Optional shape — `extStyle`, `omImport`, `columnNamingStrategy`, `apiPrefix`, `outputLayout`, and `packageOf` default if omitted. `packageOf` defaults to an empty Map (correct for flat layout; `runGen` always provides the real map). */
51
+ export type RenderContextInput = Omit<RenderContext, "extStyle" | "omImport" | "columnNamingStrategy" | "apiPrefix" | "outputLayout" | "packageOf" | "selfTarget" | "entityModuleTarget"> & {
52
+ extStyle?: ExtStyle;
53
+ omImport?: string;
54
+ columnNamingStrategy?: ColumnNamingStrategy;
55
+ apiPrefix?: string;
56
+ outputLayout?: OutputLayout;
57
+ packageOf?: Map<string, string | undefined>;
58
+ selfTarget?: ResolvedTarget;
59
+ entityModuleTarget?: ResolvedTarget;
60
+ };
61
+ /** Append the configured extension to a cross-entity module specifier. */
62
+ export declare function withExt(spec: string, style: ExtStyle): string;
63
+ /** Thin factory; applies sensible defaults for fields the caller may omit. */
64
+ export declare function makeRenderContext(opts: RenderContextInput): RenderContext;
65
+ //# sourceMappingURL=render-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-context.d.ts","sourceRoot":"","sources":["../src/render-context.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAErE;;;;;;;GAOG;AACH,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG,MAAM,CAAC;AAErC,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,QAAQ,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,QAAQ,EAAE,QAAQ,CAAC;IACnB,gGAAgG;IAChG,oBAAoB,EAAE,oBAAoB,CAAC;IAC3C,8FAA8F;IAC9F,SAAS,EAAE,MAAM,CAAC;IAClB,sHAAsH;IACtH,YAAY,EAAE,YAAY,CAAC;IAC3B,qFAAqF;IACrF,UAAU,EAAE,cAAc,CAAC;IAC3B,oEAAoE;IACpE,kBAAkB,EAAE,cAAc,CAAC;IACnC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B,iEAAiE;IACjE,WAAW,EAAE,WAAW,CAAC;IACzB,uGAAuG;IACvG,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAC5C;AAED,+OAA+O;AAC/O,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,aAAa,EAAE,UAAU,GAAG,UAAU,GAAG,sBAAsB,GAAG,WAAW,GAAG,cAAc,GAAG,WAAW,GAAG,YAAY,GAAG,oBAAoB,CAAC,GAAG;IAC1L,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,SAAS,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAC5C,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,kBAAkB,CAAC,EAAE,cAAc,CAAC;CACrC,CAAC;AAEF,0EAA0E;AAC1E,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,MAAM,CAE7D;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,kBAAkB,GAAG,aAAa,CAoBzE"}
@@ -0,0 +1,28 @@
1
+ // RenderContext — cross-cutting state passed to every template.
2
+ /** Append the configured extension to a cross-entity module specifier. */
3
+ export function withExt(spec, style) {
4
+ return style === "js" ? `${spec}.js` : spec;
5
+ }
6
+ /** Thin factory; applies sensible defaults for fields the caller may omit. */
7
+ export function makeRenderContext(opts) {
8
+ const outputLayout = opts.outputLayout ?? "flat";
9
+ const defaultTarget = opts.selfTarget ?? {
10
+ name: "default",
11
+ outDir: opts.outDir,
12
+ importBase: undefined,
13
+ outputLayout,
14
+ dbImport: opts.dbImport,
15
+ };
16
+ return {
17
+ ...opts,
18
+ extStyle: opts.extStyle ?? "none",
19
+ omImport: opts.omImport ?? "../index",
20
+ columnNamingStrategy: opts.columnNamingStrategy ?? "snake_case",
21
+ apiPrefix: opts.apiPrefix ?? "",
22
+ outputLayout,
23
+ packageOf: opts.packageOf ?? new Map(),
24
+ selfTarget: defaultTarget,
25
+ entityModuleTarget: opts.entityModuleTarget ?? defaultTarget,
26
+ };
27
+ }
28
+ //# sourceMappingURL=render-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-context.js","sourceRoot":"","sources":["../src/render-context.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAkEhE,0EAA0E;AAC1E,MAAM,UAAU,OAAO,CAAC,IAAY,EAAE,KAAe;IACnD,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9C,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,iBAAiB,CAAC,IAAwB;IACxD,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,MAAM,CAAC;IACjD,MAAM,aAAa,GAAmB,IAAI,CAAC,UAAU,IAAI;QACvD,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,UAAU,EAAE,SAAS;QACrB,YAAY;QACZ,QAAQ,EAAE,IAAI,CAAC,QAAQ;KACxB,CAAC;IACF,OAAO;QACL,GAAG,IAAI;QACP,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,MAAM;QACjC,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,UAAU;QACrC,oBAAoB,EAAE,IAAI,CAAC,oBAAoB,IAAI,YAAY;QAC/D,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,EAAE;QAC/B,YAAY;QACZ,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI,GAAG,EAAE;QACtC,UAAU,EAAE,aAAa;QACzB,kBAAkB,EAAE,IAAI,CAAC,kBAAkB,IAAI,aAAa;KAC7D,CAAC;AACJ,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { MetaData } from "@metaobjectsdev/metadata";
2
+ import type { MetaobjectsGenConfig } from "./metaobjects-config.js";
3
+ import { type WriteResult, type MergeStrategy } from "./overwrite-policy.js";
4
+ export interface RunGenOpts {
5
+ config: MetaobjectsGenConfig;
6
+ metadata: MetaData;
7
+ /** Optional whitelist of entity names. */
8
+ entityFilter?: string[];
9
+ /** Overwrite strategy passed to decideAndWrite. Defaults to "overwrite". */
10
+ mergeStrategy?: MergeStrategy;
11
+ }
12
+ export interface RunGenResult {
13
+ files: WriteResult[];
14
+ warnings: string[];
15
+ }
16
+ export declare function runGen(opts: RunGenOpts): Promise<RunGenResult>;
17
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAc,MAAM,0BAA0B,CAAC;AAGrE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAMpE,OAAO,EAAkB,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAM7F,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,oBAAoB,CAAC;IAC7B,QAAQ,EAAE,QAAQ,CAAC;IACnB,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,4EAA4E;IAC5E,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,CAmJpE"}
package/dist/runner.js ADDED
@@ -0,0 +1,135 @@
1
+ import { join } from "node:path";
2
+ import { MetaRoot } from "@metaobjectsdev/metadata";
3
+ import { normalizeConfig, DEFAULT_TARGET_NAME } from "./metaobjects-config.js";
4
+ import { buildPkMap } from "./pk-resolver.js";
5
+ import { buildRelationMap } from "./relation-resolver.js";
6
+ import { makeRenderContext } from "./render-context.js";
7
+ import { decideAndWrite } from "./overwrite-policy.js";
8
+ /** JS-identifier-shape only. Prevents filesystem traversal when metadata comes
9
+ * from untrusted sources (e.g. MCP). Mirrors the guard in legacy generate.ts. */
10
+ const VALID_ENTITY_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
11
+ export async function runGen(opts) {
12
+ const warnings = [];
13
+ const strategy = opts.mergeStrategy ?? "overwrite";
14
+ // loadMemory now returns MetaRoot; guard here also covers callers that pass a
15
+ // plain MetaData (e.g. test helpers that build trees programmatically).
16
+ if (!(opts.metadata instanceof MetaRoot)) {
17
+ throw new Error("runGen: opts.metadata must be a loaded MetaRoot.");
18
+ }
19
+ const root = opts.metadata;
20
+ // 1. Resolve entities (filter + safety check).
21
+ const allObjects = root.objects();
22
+ const entityFilter = opts.entityFilter;
23
+ const filtered = entityFilter
24
+ ? allObjects.filter((o) => entityFilter.includes(o.name))
25
+ : allObjects;
26
+ if (filtered.length === 0) {
27
+ const reason = opts.entityFilter
28
+ ? "no object children match the provided entityFilter"
29
+ : "root has no object children";
30
+ warnings.push(`No entities to generate — ${reason}.`);
31
+ return { files: [], warnings };
32
+ }
33
+ const safeEntities = [];
34
+ for (const entity of filtered) {
35
+ if (!VALID_ENTITY_NAME.test(entity.name)) {
36
+ warnings.push(`Skipping entity with unsafe name "${entity.name}" — must match /^[A-Za-z_][A-Za-z0-9_]*$/.`);
37
+ continue;
38
+ }
39
+ safeEntities.push(entity);
40
+ }
41
+ if (safeEntities.length === 0) {
42
+ return { files: [], warnings };
43
+ }
44
+ // 2. Resolve targets + entity-module target.
45
+ const config = normalizeConfig(opts.config);
46
+ const targets = config.targets;
47
+ const targetOf = (g) => {
48
+ const name = g.target ?? DEFAULT_TARGET_NAME;
49
+ const t = targets[name];
50
+ if (!t) {
51
+ throw new Error(`Generator "${g.name}" references unknown target "${name}". ` +
52
+ `Valid targets: ${Object.keys(targets).join(", ")}.`);
53
+ }
54
+ return t;
55
+ };
56
+ // Validate all target references up front.
57
+ for (const g of config.generators)
58
+ targetOf(g);
59
+ const entityGen = config.generators.find((g) => g.emitsEntityModule);
60
+ const entityModuleTarget = entityGen ? targetOf(entityGen) : targets[DEFAULT_TARGET_NAME];
61
+ const needsCrossTarget = config.generators.some((g) => (g.target ?? DEFAULT_TARGET_NAME) !== entityModuleTarget.name);
62
+ if (needsCrossTarget && entityModuleTarget.importBase === undefined) {
63
+ throw new Error(`Target "${entityModuleTarget.name}" holds the entity modules that other ` +
64
+ `targets import, but has no importBase. Set importBase on it (e.g. ` +
65
+ `"@your-pkg/database/generated").`);
66
+ }
67
+ // 3. Build shared render state once.
68
+ const pkMap = buildPkMap(root);
69
+ const relationMap = buildRelationMap(root);
70
+ const packageOf = new Map(root.objects().map((o) => [o.name, o.package]));
71
+ // 4. Run each generator with a per-target render context; collect with full path.
72
+ const emitted = [];
73
+ for (const generator of config.generators) {
74
+ const selfTarget = targetOf(generator);
75
+ const renderContext = makeRenderContext({
76
+ dialect: config.dialect,
77
+ loadedRoot: root,
78
+ outDir: selfTarget.outDir,
79
+ dbImport: selfTarget.dbImport,
80
+ extStyle: config.extStyle,
81
+ columnNamingStrategy: config.columnNamingStrategy,
82
+ apiPrefix: config.apiPrefix,
83
+ outputLayout: selfTarget.outputLayout,
84
+ pkMap,
85
+ relationMap,
86
+ packageOf,
87
+ selfTarget,
88
+ entityModuleTarget,
89
+ });
90
+ const ctx = {
91
+ entities: safeEntities,
92
+ loadedRoot: root,
93
+ matches: (e) => generator.filter?.(e) ?? true,
94
+ config: {
95
+ outDir: selfTarget.outDir,
96
+ extStyle: config.extStyle,
97
+ dbImport: selfTarget.dbImport,
98
+ dialect: config.dialect,
99
+ outputLayout: selfTarget.outputLayout,
100
+ },
101
+ renderContext,
102
+ warn: (msg) => warnings.push(`[${generator.name}] ${msg}`),
103
+ };
104
+ let files;
105
+ try {
106
+ files = await generator.generate(ctx);
107
+ }
108
+ catch (err) {
109
+ const msg = err instanceof Error ? err.message : String(err);
110
+ throw new Error(`[${generator.name}] ${msg}`);
111
+ }
112
+ for (const file of files) {
113
+ const fullPath = join(selfTarget.outDir, file.path);
114
+ const collision = emitted.find((prev) => prev.fullPath === fullPath);
115
+ if (collision) {
116
+ throw new Error(`Output path collision: "${fullPath}" emitted by both ` +
117
+ `"${collision.generatedBy}" and "${generator.name}". ` +
118
+ `Adjust one generator's filter or output path.`);
119
+ }
120
+ emitted.push({ fullPath, content: file.content, generatedBy: generator.name });
121
+ }
122
+ }
123
+ // 5. Write phase.
124
+ const writes = [];
125
+ for (const file of emitted) {
126
+ const result = decideAndWrite(file.fullPath, file.content, strategy);
127
+ writes.push(result);
128
+ if (result.status === "refused") {
129
+ warnings.push(`Refused to overwrite ${file.fullPath}: file exists without @generated header. ` +
130
+ `Move to a different outDir, delete the file, or add the header to opt in.`);
131
+ }
132
+ }
133
+ return { files: writes, warnings };
134
+ }
135
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { type ExtStyle } from "../render-context.js";
2
+ import { type ResolvedTarget } from "../import-path.js";
3
+ export interface BarrelEntry {
4
+ name: string;
5
+ package: string | undefined;
6
+ }
7
+ export declare function renderBarrel(entries: BarrelEntry[], extStyle: ExtStyle, selfTarget: ResolvedTarget, entityModuleTarget: ResolvedTarget): string;
8
+ //# sourceMappingURL=barrel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"barrel.d.ts","sourceRoot":"","sources":["../../src/templates/barrel.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAyB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAE/E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED,wBAAgB,YAAY,CAC1B,OAAO,EAAE,WAAW,EAAE,EACtB,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,cAAc,EAC1B,kBAAkB,EAAE,cAAc,GACjC,MAAM,CAMR"}
@@ -0,0 +1,12 @@
1
+ // Barrel template — emits index.ts with one export per entity, alphabetical.
2
+ import { GENERATED_HEADER } from "../constants.js";
3
+ import {} from "../render-context.js";
4
+ import { barrelModuleSpecifier } from "../import-path.js";
5
+ export function renderBarrel(entries, extStyle, selfTarget, entityModuleTarget) {
6
+ const sorted = [...entries].sort((a, b) => a.name.localeCompare(b.name));
7
+ const exports = sorted
8
+ .map((e) => `export * from ${JSON.stringify(barrelModuleSpecifier(selfTarget, entityModuleTarget, e.package, e.name, extStyle))};`)
9
+ .join("\n");
10
+ return `// ${GENERATED_HEADER} — DO NOT EDIT.\n${exports}\n`;
11
+ }
12
+ //# sourceMappingURL=barrel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"barrel.js","sourceRoot":"","sources":["../../src/templates/barrel.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAE7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAiB,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAuB,MAAM,mBAAmB,CAAC;AAO/E,MAAM,UAAU,YAAY,CAC1B,OAAsB,EACtB,QAAkB,EAClB,UAA0B,EAC1B,kBAAkC;IAElC,MAAM,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACzE,MAAM,OAAO,GAAG,MAAM;SACnB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAAC,UAAU,EAAE,kBAAkB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,GAAG,CAAC;SAClI,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,OAAO,MAAM,gBAAgB,oBAAoB,OAAO,IAAI,CAAC;AAC/D,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { type Code } from "ts-poet";
2
+ import { MetaObject } from "@metaobjectsdev/metadata";
3
+ import { type RenderContext } from "../render-context.js";
4
+ /**
5
+ * Render the Drizzle table definition for one entity, including:
6
+ * - FK .references() on FK columns derived from relationship children
7
+ * - relations() block at the end of the section
8
+ *
9
+ * Returns a Code object so ts-poet can deduplicate imports when this composes
10
+ * with the rest of the entity file. Biome formatting runs after composition.
11
+ */
12
+ export declare function renderDrizzleSchema(obj: MetaObject, ctx: RenderContext): Code;
13
+ //# sourceMappingURL=drizzle-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drizzle-schema.d.ts","sourceRoot":"","sources":["../../src/templates/drizzle-schema.ts"],"names":[],"mappings":"AAIA,OAAO,EAAuB,KAAK,IAAI,EAAE,MAAM,SAAS,CAAC;AACzD,OAAO,EAAE,UAAU,EAAa,MAAM,0BAA0B,CAAC;AAOjE,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAM1D;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,GAAG,IAAI,CA8F7E"}