firebase-contract 0.1.0-rc.8 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +141 -64
  2. package/dist/lib/_internal/compiler/compiler.d.ts.map +1 -1
  3. package/dist/lib/_internal/compiler/compiler.js +50 -21
  4. package/dist/lib/_internal/generators/api/api-dto-generator.d.ts.map +1 -1
  5. package/dist/lib/_internal/generators/api/api-dto-generator.js +1 -8
  6. package/dist/lib/_internal/generators/api/api-types-generator.d.ts.map +1 -1
  7. package/dist/lib/_internal/generators/api/api-types-generator.js +11 -5
  8. package/dist/lib/_internal/generators/api/task-payload-generator.js +3 -3
  9. package/dist/lib/_internal/generators/data-connect/graphql-generator.d.ts +0 -7
  10. package/dist/lib/_internal/generators/data-connect/graphql-generator.d.ts.map +1 -1
  11. package/dist/lib/_internal/generators/data-connect/graphql-generator.js +5 -4
  12. package/dist/lib/_internal/generators/data-connect/operations-generator.d.ts +0 -7
  13. package/dist/lib/_internal/generators/data-connect/operations-generator.d.ts.map +1 -1
  14. package/dist/lib/_internal/generators/data-connect/operations-generator.js +8 -7
  15. package/dist/lib/_internal/generators/firestore/firestore-projection-generator.d.ts.map +1 -1
  16. package/dist/lib/_internal/generators/firestore/firestore-projection-generator.js +7 -22
  17. package/dist/lib/_internal/generators/firestore/firestore-split-generator.d.ts +1 -1
  18. package/dist/lib/_internal/generators/firestore/firestore-split-generator.d.ts.map +1 -1
  19. package/dist/lib/_internal/generators/firestore/firestore-split-generator.js +69 -47
  20. package/dist/lib/_internal/generators/generator.d.ts +2 -0
  21. package/dist/lib/_internal/generators/generator.d.ts.map +1 -1
  22. package/dist/lib/_internal/generators/index.d.ts +2 -5
  23. package/dist/lib/_internal/generators/index.d.ts.map +1 -1
  24. package/dist/lib/_internal/generators/index.js +4 -15
  25. package/dist/lib/_internal/generators/sql/sql-migration-generator.d.ts +2 -1
  26. package/dist/lib/_internal/generators/sql/sql-migration-generator.d.ts.map +1 -1
  27. package/dist/lib/_internal/generators/sql/sql-migration-generator.js +11 -4
  28. package/dist/lib/_internal/generators/support/jsdoc.d.ts +3 -0
  29. package/dist/lib/_internal/generators/support/jsdoc.d.ts.map +1 -0
  30. package/dist/lib/_internal/generators/support/jsdoc.js +9 -0
  31. package/dist/lib/_internal/generators/support/split-variant.d.ts +9 -0
  32. package/dist/lib/_internal/generators/support/split-variant.d.ts.map +1 -0
  33. package/dist/lib/_internal/generators/support/split-variant.js +13 -0
  34. package/dist/lib/_internal/generators/support/split.d.ts +1 -1
  35. package/dist/lib/_internal/generators/support/split.d.ts.map +1 -1
  36. package/dist/lib/_internal/generators/support/split.js +3 -2
  37. package/dist/lib/_internal/generators/typescript/render.d.ts +15 -0
  38. package/dist/lib/_internal/generators/typescript/render.d.ts.map +1 -0
  39. package/dist/lib/_internal/generators/typescript/render.js +92 -0
  40. package/dist/lib/_internal/generators/typescript/typescript-generator.d.ts +2 -1
  41. package/dist/lib/_internal/generators/typescript/typescript-generator.d.ts.map +1 -1
  42. package/dist/lib/_internal/generators/typescript/typescript-generator.js +12 -78
  43. package/dist/lib/_internal/generators/typescript/typescript-split-generator.d.ts +1 -1
  44. package/dist/lib/_internal/generators/typescript/typescript-split-generator.d.ts.map +1 -1
  45. package/dist/lib/_internal/generators/typescript/typescript-split-generator.js +4 -84
  46. package/dist/lib/_internal/generators/zod/render.d.ts +12 -0
  47. package/dist/lib/_internal/generators/zod/render.d.ts.map +1 -0
  48. package/dist/lib/_internal/generators/zod/render.js +59 -0
  49. package/dist/lib/_internal/generators/zod/zod-generator.d.ts +2 -1
  50. package/dist/lib/_internal/generators/zod/zod-generator.d.ts.map +1 -1
  51. package/dist/lib/_internal/generators/zod/zod-generator.js +11 -55
  52. package/dist/lib/_internal/generators/zod/zod-split-generator.d.ts +1 -1
  53. package/dist/lib/_internal/generators/zod/zod-split-generator.d.ts.map +1 -1
  54. package/dist/lib/_internal/generators/zod/zod-split-generator.js +4 -56
  55. package/dist/lib/_internal/ir/build-ir.d.ts.map +1 -1
  56. package/dist/lib/_internal/ir/build-ir.js +93 -8
  57. package/dist/lib/_internal/ir/ir.d.ts +18 -5
  58. package/dist/lib/_internal/ir/ir.d.ts.map +1 -1
  59. package/dist/lib/_internal/ir/ir.js +1 -0
  60. package/dist/lib/_internal/parser/parse.d.ts +5 -3
  61. package/dist/lib/_internal/parser/parse.d.ts.map +1 -1
  62. package/dist/lib/_internal/parser/parse.js +103 -14
  63. package/dist/lib/_internal/parser/raw-document.d.ts +18 -2
  64. package/dist/lib/_internal/parser/raw-document.d.ts.map +1 -1
  65. package/dist/lib/_internal/resolver/import-resolver.js +1 -1
  66. package/dist/lib/_internal/validation/rules.d.ts +39 -0
  67. package/dist/lib/_internal/validation/rules.d.ts.map +1 -1
  68. package/dist/lib/_internal/validation/rules.js +133 -1
  69. package/dist/lib/cli/commands/generate.d.ts.map +1 -1
  70. package/dist/lib/cli/commands/generate.js +6 -4
  71. package/dist/lib/cli/commands/index.d.ts.map +1 -1
  72. package/dist/lib/cli/commands/index.js +2 -0
  73. package/dist/lib/cli/commands/init.d.ts.map +1 -1
  74. package/dist/lib/cli/commands/init.js +2 -18
  75. package/dist/lib/cli/commands/where.d.ts +3 -0
  76. package/dist/lib/cli/commands/where.d.ts.map +1 -0
  77. package/dist/lib/cli/commands/where.js +57 -0
  78. package/dist/lib/cli/config.d.ts +3 -2
  79. package/dist/lib/cli/config.d.ts.map +1 -1
  80. package/dist/lib/cli/config.js +3 -0
  81. package/package.json +1 -1
@@ -2,6 +2,7 @@ import { headerBlocks } from '../support/header.js';
2
2
  import { constantCase, pluralize, snakeCase } from '../support/naming.js';
3
3
  import { isRelation } from '../support/relations.js';
4
4
  import { outputFile } from '../support/templates.js';
5
+ import { withSplitVariant } from '../support/split-variant.js';
5
6
  // Data Connect built-in scalars. `json` and embedded model objects are stored
6
7
  // as `Any` — the logical type is preserved in a comment and restored by the
7
8
  // Data Connect adapter generator.
@@ -131,7 +132,7 @@ const commentBanner = (header) => header
131
132
  * model fields become the `Any` scalar while retaining their logical type as a
132
133
  * comment, matching the "JSON is Any, logical type preserved" rule.
133
134
  */
134
- export const createDataConnectGraphqlGenerator = () => ({
135
+ export const createDataConnectGraphqlGenerator = () => withSplitVariant({
135
136
  name: 'data-connect-graphql',
136
137
  description: 'Data Connect GraphQL schema',
137
138
  generate(ir, context) {
@@ -154,7 +155,7 @@ export const createDataConnectGraphqlGenerator = () => ({
154
155
  }
155
156
  return [{ path: outputFile(context, 'schema.gql'), content: `${blocks.join('\n\n')}\n` }];
156
157
  },
157
- });
158
+ }, createDataConnectGraphqlSplitLayout());
158
159
  const renderEnumGql = (irEnum, rename) => {
159
160
  const lines = [...commentLines(irEnum.description, '')];
160
161
  lines.push(`enum ${gqlOf(rename, irEnum.name)} {`);
@@ -171,8 +172,8 @@ const renderEnumGql = (irEnum, rename) => {
171
172
  * model that references it (leftover enums go to `schema/_enums.gql`). The `Any`
172
173
  * scalar is not declared — Data Connect provides it built-in.
173
174
  */
174
- export const createDataConnectGraphqlSplitGenerator = () => ({
175
- name: 'data-connect-graphql-split',
175
+ const createDataConnectGraphqlSplitLayout = () => ({
176
+ name: 'data-connect-graphql',
176
177
  description: 'Data Connect GraphQL schema, one file per table (schema/<table>.gql)',
177
178
  generate(ir, context) {
178
179
  const banner = headerBlocks(context, commentBanner);
@@ -8,11 +8,4 @@ import { Generator } from '../generator.js';
8
8
  * `<connector>/operations.gql`. Operations without a connector go to the root.
9
9
  */
10
10
  export declare const createDataConnectOperationsGenerator: () => Generator;
11
- /**
12
- * Split variant matching the real connector layout:
13
- * `<connector>/operations/<entity>/{queries,mutations}.gql` (+ one
14
- * `operations-types.ts` per connector). Selectable alongside the single-file
15
- * `data-connect-operations` generator.
16
- */
17
- export declare const createDataConnectOperationsSplitGenerator: () => Generator;
18
11
  //# sourceMappingURL=operations-generator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"operations-generator.d.ts","sourceRoot":"","sources":["../../../../../src/lib/_internal/generators/data-connect/operations-generator.ts"],"names":[],"mappings":"AAGA,OAAO,EAAiB,SAAS,EAAoB,MAAM,iBAAiB,CAAA;AAkf5E;;;;;;;GAOG;AACH,eAAO,MAAM,oCAAoC,QAAO,SAsBtD,CAAA;AAMF;;;;;GAKG;AACH,eAAO,MAAM,yCAAyC,QAAO,SAmC3D,CAAA"}
1
+ {"version":3,"file":"operations-generator.d.ts","sourceRoot":"","sources":["../../../../../src/lib/_internal/generators/data-connect/operations-generator.ts"],"names":[],"mappings":"AAGA,OAAO,EAAiB,SAAS,EAAoB,MAAM,iBAAiB,CAAA;AAmf5E;;;;;;;GAOG;AACH,eAAO,MAAM,oCAAoC,QAAO,SA0BrD,CAAA"}
@@ -2,6 +2,7 @@ import { findModel } from '../../ir/ir.js';
2
2
  import { camelCase, pluralize, snakeCase } from '../support/naming.js';
3
3
  import { isRelation, relationFkName, relationFkType } from '../support/relations.js';
4
4
  import { headerBlocks } from '../support/header.js';
5
+ import { withSplitVariant } from '../support/split-variant.js';
5
6
  const SCALAR_GQL = {
6
7
  string: 'String',
7
8
  int: 'Int',
@@ -473,7 +474,7 @@ const renderTsFile = (ir, entries, typesImport, context) => {
473
474
  * several connectors (`connectors: [app, api]`) and is emitted into each, under
474
475
  * `<connector>/operations.gql`. Operations without a connector go to the root.
475
476
  */
476
- export const createDataConnectOperationsGenerator = () => ({
477
+ export const createDataConnectOperationsGenerator = () => withSplitVariant({
477
478
  name: 'data-connect-operations',
478
479
  description: 'Data Connect query/mutation operations (.gql + TS types), routed per connector',
479
480
  generate(ir, context) {
@@ -493,17 +494,17 @@ export const createDataConnectOperationsGenerator = () => ({
493
494
  }
494
495
  return files;
495
496
  },
496
- });
497
+ }, createDataConnectOperationsSplitLayout());
497
498
  /** Entity directory name for a model: kebab-case of its table name. */
498
499
  const entityDir = (model) => (model.table ?? pluralize(snakeCase(model.name))).replace(/_/g, '-');
499
500
  /**
500
- * Split variant matching the real connector layout:
501
+ * Split layout matching the real connector repo structure:
501
502
  * `<connector>/operations/<entity>/{queries,mutations}.gql` (+ one
502
- * `operations-types.ts` per connector). Selectable alongside the single-file
503
- * `data-connect-operations` generator.
503
+ * `operations-types.ts` per connector). Selected via `split: true` on the
504
+ * `data-connect-operations` declaration.
504
505
  */
505
- export const createDataConnectOperationsSplitGenerator = () => ({
506
- name: 'data-connect-operations-split',
506
+ const createDataConnectOperationsSplitLayout = () => ({
507
+ name: 'data-connect-operations',
507
508
  description: 'Data Connect operations, one file per entity per connector',
508
509
  generate(ir, context) {
509
510
  if (ir.operations.length === 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"firestore-projection-generator.d.ts","sourceRoot":"","sources":["../../../../../src/lib/_internal/generators/firestore/firestore-projection-generator.ts"],"names":[],"mappings":"AAGA,OAAO,EAAiB,SAAS,EAAoB,MAAM,iBAAiB,CAAA;AAwF5E;;;;;;;GAOG;AACH,eAAO,MAAM,kCAAkC,QAAO,SAgBpD,CAAA"}
1
+ {"version":3,"file":"firestore-projection-generator.d.ts","sourceRoot":"","sources":["../../../../../src/lib/_internal/generators/firestore/firestore-projection-generator.ts"],"names":[],"mappings":"AAGA,OAAO,EAAiB,SAAS,EAAoB,MAAM,iBAAiB,CAAA;AA4E5E;;;;;;;GAOG;AACH,eAAO,MAAM,kCAAkC,QAAO,SAiBnD,CAAA"}
@@ -1,8 +1,10 @@
1
- import { findModel } from '../../ir/ir.js';
1
+ import { findFragment, findModel } from '../../ir/ir.js';
2
2
  import { singleQuote } from '../support/naming.js';
3
3
  import { isRelation, relationFkName } from '../support/relations.js';
4
4
  import { headerBlocks } from '../support/header.js';
5
5
  import { outputFile } from '../support/templates.js';
6
+ import { withSplitVariant } from '../support/split-variant.js';
7
+ import { createFirestoreSplitLayout } from './firestore-split-generator.js';
6
8
  // Firestore stores timestamps as dates and represents relations as resolved
7
9
  // (hashids-encoded) string ids, regardless of the Data Connect id's type.
8
10
  const SCALAR_ZOD = {
@@ -16,19 +18,6 @@ const SCALAR_ZOD = {
16
18
  json: 'z.unknown()',
17
19
  id: 'z.string()',
18
20
  };
19
- const META_BLOCK = [
20
- 'export const _Meta_OperationSchema = z.object({',
21
- ' id: z.string(),',
22
- ' identifierId: z.string(),',
23
- ' updatedAt: z.number(),',
24
- '})',
25
- 'export const _Meta_Schema = z.object({',
26
- ' scope: z.number().nullable(),',
27
- ' sys: z.number().nullable(),',
28
- ' op: _Meta_OperationSchema.nullable(),',
29
- '})',
30
- 'export type _Meta_ = z.infer<typeof _Meta_Schema>',
31
- ].join('\n');
32
21
  const fsField = (ir, field) => {
33
22
  const name = isRelation(field) ? relationFkName(field) : field.name;
34
23
  let schema;
@@ -67,7 +56,8 @@ const effectiveFields = (ir, doc) => {
67
56
  const { name, schema } = fsField(ir, field);
68
57
  entries.set(name, schema);
69
58
  }
70
- for (const field of doc.fields) {
59
+ const added = [...doc.fields, ...doc.extends.flatMap(name => findFragment(ir, name)?.fields ?? [])];
60
+ for (const field of added) {
71
61
  const { name, schema } = fsField(ir, field);
72
62
  entries.set(name, schema);
73
63
  }
@@ -80,8 +70,6 @@ const renderDoc = (ir, doc) => {
80
70
  else if (doc.description)
81
71
  lines.push(`// ${doc.description}`);
82
72
  const fields = effectiveFields(ir, doc).map(field => ` ${field.name}: ${field.schema},`);
83
- if (doc.meta)
84
- fields.push(' _meta_: _Meta_Schema,');
85
73
  lines.push(`export const ${doc.name}Schema = z.object({\n${fields.join('\n')}\n})`);
86
74
  lines.push(`export type ${doc.name} = z.infer<typeof ${doc.name}Schema>`);
87
75
  return lines.join('\n');
@@ -94,7 +82,7 @@ const renderDoc = (ir, doc) => {
94
82
  * add denormalized data (e.g. flattened graph edges) — mirroring a
95
83
  * hand-written Firestore schema library.
96
84
  */
97
- export const createFirestoreProjectionGenerator = () => ({
85
+ export const createFirestoreProjectionGenerator = () => withSplitVariant({
98
86
  name: 'firestore',
99
87
  description: 'Firestore projection Zod schemas (derived from Data Connect models)',
100
88
  generate(ir, context) {
@@ -102,12 +90,9 @@ export const createFirestoreProjectionGenerator = () => ({
102
90
  return [];
103
91
  }
104
92
  const blocks = [...headerBlocks(context), "import { z } from 'zod'"];
105
- if (ir.firestore.some(doc => doc.meta)) {
106
- blocks.push(META_BLOCK);
107
- }
108
93
  for (const doc of ir.firestore) {
109
94
  blocks.push(renderDoc(ir, doc));
110
95
  }
111
96
  return [{ path: outputFile(context, 'firestore.ts'), content: `${blocks.join('\n\n')}\n` }];
112
97
  },
113
- });
98
+ }, createFirestoreSplitLayout());
@@ -7,5 +7,5 @@ import { Generator } from '../generator.js';
7
7
  * the `_meta_` envelope under `firestore/_/`, and a `firestore.ts` barrel that
8
8
  * also carries the `FIRESTORE_DATABASES` constants.
9
9
  */
10
- export declare const createFirestoreSplitGenerator: () => Generator;
10
+ export declare const createFirestoreSplitLayout: () => Generator;
11
11
  //# sourceMappingURL=firestore-split-generator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"firestore-split-generator.d.ts","sourceRoot":"","sources":["../../../../../src/lib/_internal/generators/firestore/firestore-split-generator.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,SAAS,EAAoB,MAAM,iBAAiB,CAAA;AAyO5E;;;;;;;GAOG;AACH,eAAO,MAAM,6BAA6B,QAAO,SAqI/C,CAAA"}
1
+ {"version":3,"file":"firestore-split-generator.d.ts","sourceRoot":"","sources":["../../../../../src/lib/_internal/generators/firestore/firestore-split-generator.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,SAAS,EAAoB,MAAM,iBAAiB,CAAA;AAoN5E;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,QAAO,SAiL5C,CAAA"}
@@ -1,10 +1,16 @@
1
- import { findEnum, findModel } from '../../ir/ir.js';
1
+ import { findEnum, findFragment, findModel } from '../../ir/ir.js';
2
2
  import { zodConstraints } from '../support/constraints.js';
3
3
  import { headerBlocks } from '../support/header.js';
4
4
  import { camelCase, constantCase, pluralize, singleQuote } from '../support/naming.js';
5
5
  import { isRelation, relationFkName } from '../support/relations.js';
6
6
  import { collectDeps, kebabCase, tableNameOf } from '../support/split.js';
7
7
  import { outputFile } from '../support/templates.js';
8
+ /** Fields spliced into a projection by its `extends` fragments (in declared order). */
9
+ const fragmentFieldsOf = (ir, doc) => doc.extends.flatMap(name => findFragment(ir, name)?.fields ?? []);
10
+ /** True for a placement-pinned value object (emitted at its own `out`, not co-located). */
11
+ const isPinnedModel = (ir, name) => findModel(ir, name)?.out !== undefined;
12
+ /** Import path (dir barrel) from a `firestore/<name>.ts` doc to a pinned model's `out` dir. */
13
+ const pinnedImportPath = (out) => `./${out.startsWith('firestore/') ? out.slice('firestore/'.length) : out}`;
8
14
  // Projection-level scalars: timestamps become dates, ids/relations become
9
15
  // resolved (hashids-encoded) string ids.
10
16
  const SCALAR_FS = {
@@ -24,36 +30,6 @@ const SCALAR_EMBEDDED = {
24
30
  int: 'z.number().int()',
25
31
  int64: 'z.number().int()',
26
32
  };
27
- const META_FILE = `import { z } from 'zod'
28
-
29
- /**
30
- * Cloud Task 整合性管理用の operation 識別子メタ。
31
- * 整合性回復 (reconciliation) 処理の最新世代を表す。
32
- */
33
- export const _Meta_OperationSchema = z.object({
34
- /** opId (operation id) */
35
- id: z.string(),
36
- /** 整合性回復処理の識別子 */
37
- identifierId: z.string(),
38
- /** unixtime (最終更新) */
39
- updatedAt: z.number(),
40
- })
41
- export type _Meta_Operation = z.infer<typeof _Meta_OperationSchema>
42
-
43
- /**
44
- * 全 Firestore document に付ける整合性管理メタ。
45
- *
46
- * - \`scope\`: app hosting 経由で set される unixtime
47
- * - \`sys\`: cloud functions / cloud task 経由で set される unixtime
48
- * - \`op\`: cloud task で set される operation メタ
49
- */
50
- export const _Meta_Schema = z.object({
51
- scope: z.number().nullable(),
52
- sys: z.number().nullable(),
53
- op: _Meta_OperationSchema.nullable(),
54
- })
55
- export type _Meta_ = z.infer<typeof _Meta_Schema>
56
- `;
57
33
  const lineComments = (description, indent) => description ? description.split('\n').map(line => `${indent}// ${line}`.trimEnd()) : [];
58
34
  const jsdoc = (description, indent) => {
59
35
  if (!description)
@@ -123,7 +99,7 @@ const renderEmbeddedModel = (ir, model) => {
123
99
  lines.push(`export type ${name} = z.infer<typeof ${name}Schema>`);
124
100
  return lines.join('\n');
125
101
  };
126
- // zod-split と同じ DC 側チェーン。FS 側と文字列一致するフィールドは
102
+ // zod の split layout と同じ DC 側チェーン。FS 側と文字列一致するフィールドは
127
103
  // `.pick()` で DC スキーマから流用できる。
128
104
  const SCALAR_DC = {
129
105
  string: 'z.string()',
@@ -216,7 +192,7 @@ const effectiveFields = (ir, doc) => {
216
192
  entries.set(name, { comments: [], entry: ` ${name}: ${schema},`, name, pickable });
217
193
  roots.push(field);
218
194
  }
219
- for (const field of doc.fields) {
195
+ for (const field of [...doc.fields, ...fragmentFieldsOf(ir, doc)]) {
220
196
  const { name, schema } = overrideFieldSchema(ir, field);
221
197
  const comments = field.jsdoc ? jsdoc(field.description, ' ') : lineComments(field.description, ' ');
222
198
  entries.set(name, { comments, entry: ` ${name}: ${schema},`, name, pickable: false });
@@ -233,8 +209,8 @@ const docModuleName = (doc) => camelCase(pluralize(doc.name));
233
209
  * the `_meta_` envelope under `firestore/_/`, and a `firestore.ts` barrel that
234
210
  * also carries the `FIRESTORE_DATABASES` constants.
235
211
  */
236
- export const createFirestoreSplitGenerator = () => ({
237
- name: 'firestore-split',
212
+ export const createFirestoreSplitLayout = () => ({
213
+ name: 'firestore',
238
214
  description: 'Firestore projection Zod schemas, one file per collection (firestore/<name>.ts + firestore.ts barrel)',
239
215
  generate(ir, context) {
240
216
  if (ir.firestore.length === 0)
@@ -249,7 +225,9 @@ export const createFirestoreSplitGenerator = () => ({
249
225
  const pseudo = { name: `__${doc.name}`, fields: roots, key: [], indexes: [] };
250
226
  const collected = collectDeps(ir, [pseudo]);
251
227
  const included = (doc.include ?? []).filter(name => !collected.models.includes(name));
252
- const deps = { enums: collected.enums, models: [...included, ...collected.models] };
228
+ // Pinned value objects are emitted at their own `out`, never co-located.
229
+ const models = [...included, ...collected.models].filter(name => !isPinnedModel(ir, name));
230
+ const deps = { enums: collected.enums, models };
253
231
  docDeps.set(doc.name, deps);
254
232
  const moduleName = docModuleName(doc);
255
233
  for (const name of deps.enums)
@@ -285,8 +263,23 @@ export const createFirestoreSplitGenerator = () => ({
285
263
  const importLines = [...external.entries()]
286
264
  .sort(([a], [b]) => a.localeCompare(b))
287
265
  .map(([home, symbols]) => `import { ${[...symbols].sort().join(', ')} } from './${home}'`);
288
- if (doc.meta)
289
- importLines.unshift(`import { _Meta_Schema } from './_'`);
266
+ // Pinned value objects directly referenced by this doc: imported from their out-dir barrel.
267
+ const { roots } = effectiveFields(ir, doc);
268
+ const pinnedImports = new Map();
269
+ for (const field of roots) {
270
+ if (field.type.kind === 'model' && isPinnedModel(ir, field.type.name)) {
271
+ const model = findModel(ir, field.type.name);
272
+ if (!model?.out)
273
+ continue;
274
+ const from = pinnedImportPath(model.out);
275
+ const set = pinnedImports.get(from) ?? new Set();
276
+ set.add(`${fsModelName(ir, field.type.name)}Schema`);
277
+ pinnedImports.set(from, set);
278
+ }
279
+ }
280
+ for (const [from, symbols] of [...pinnedImports.entries()].sort(([a], [b]) => a.localeCompare(b))) {
281
+ importLines.unshift(`import { ${[...symbols].sort().join(', ')} } from '${from}'`);
282
+ }
290
283
  // Fields whose Zod chain is identical to the Data Connect schema are
291
284
  // picked from it (single definition); only representation changes extend.
292
285
  const baseModel = doc.from ? findModel(ir, doc.from) : undefined;
@@ -330,8 +323,6 @@ export const createFirestoreSplitGenerator = () => ({
330
323
  lines.push(...field.comments);
331
324
  lines.push(field.entry);
332
325
  }
333
- if (doc.meta)
334
- lines.push(' _meta_: _Meta_Schema,');
335
326
  lines.push('})');
336
327
  }
337
328
  else {
@@ -340,17 +331,48 @@ export const createFirestoreSplitGenerator = () => ({
340
331
  lines.push(...field.comments);
341
332
  lines.push(field.entry);
342
333
  }
343
- if (doc.meta)
344
- lines.push(' _meta_: _Meta_Schema,');
345
334
  lines.push('})');
346
335
  }
347
336
  lines.push(`export type ${doc.name} = z.infer<typeof ${doc.name}Schema>`);
348
337
  blocks.push(lines.join('\n'));
349
338
  files.push({ path: `firestore/${moduleName}.ts`, content: `${blocks.join('\n\n')}\n` });
350
339
  }
351
- if (ir.firestore.some(doc => doc.meta)) {
352
- files.push({ path: 'firestore/_/_meta_.ts', content: META_FILE });
353
- files.push({ path: 'firestore/_/index.ts', content: `export * from './_meta_'\n` });
340
+ // Pinned value objects: emitted at their own `out` (grouped per out+file),
341
+ // dependencies first, with an index barrel. These replace the former
342
+ // hardcoded `_meta_` envelope — the shape now lives entirely in the contract.
343
+ const pinDirs = [];
344
+ const pinGroups = new Map();
345
+ for (const model of ir.models) {
346
+ if (model.out === undefined)
347
+ continue;
348
+ const file = model.file ?? 'index.ts';
349
+ const key = `${model.out}${file}`;
350
+ const group = pinGroups.get(key) ?? { out: model.out, file, models: [] };
351
+ group.models.push(model);
352
+ pinGroups.set(key, group);
353
+ }
354
+ for (const { out, file, models } of pinGroups.values()) {
355
+ // Order so a referenced value object is declared before its referrer (same file).
356
+ const ordered = [];
357
+ const placed = new Set();
358
+ const place = (m) => {
359
+ if (placed.has(m.name))
360
+ return;
361
+ placed.add(m.name);
362
+ for (const dep of collectDeps(ir, [m]).models) {
363
+ const depModel = models.find(x => x.name === dep);
364
+ if (depModel)
365
+ place(depModel);
366
+ }
367
+ ordered.push(m);
368
+ };
369
+ for (const m of models)
370
+ place(m);
371
+ const body = ['import { z }' + " from 'zod'", ...ordered.map(m => renderEmbeddedModel(ir, m))].join('\n\n');
372
+ const base = file.replace(/\.ts$/, '');
373
+ files.push({ path: `${out}/${file}`, content: `${body}\n` });
374
+ files.push({ path: `${out}/index.ts`, content: `export * from './${base}'\n` });
375
+ pinDirs.push(out);
354
376
  }
355
377
  // Barrel: database name constants (when the project defines services) + re-exports.
356
378
  const barrel = [...headerBlocks(context)];
@@ -367,8 +389,8 @@ export const createFirestoreSplitGenerator = () => ({
367
389
  ].join('\n'));
368
390
  }
369
391
  const exportLines = ir.firestore.map(doc => `export * from './firestore/${docModuleName(doc)}'`);
370
- if (ir.firestore.some(doc => doc.meta))
371
- exportLines.push(`export * from './firestore/_'`);
392
+ for (const out of pinDirs)
393
+ exportLines.push(`export * from './${out}'`);
372
394
  barrel.push(['// Zod schema / TypeScript 型のエクスポート', ...exportLines].join('\n'));
373
395
  files.push({ path: outputFile(context, 'firestore.ts'), content: `${barrel.join('\n\n')}\n` });
374
396
  return files;
@@ -50,6 +50,8 @@ export interface Generator {
50
50
  file: string;
51
51
  split: boolean;
52
52
  };
53
+ /** Document scope: true when `split: true` switches to a per-item file layout. */
54
+ readonly splittable?: boolean;
53
55
  generate(ir: Ir, context?: GeneratorContext): GeneratedFile[];
54
56
  }
55
57
  /** Narrow `ir.apis` to the compiler-selected subset (no-op without a filter). */
@@ -1 +1 @@
1
- {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../../../src/lib/_internal/generators/generator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAEhC,MAAM,WAAW,aAAa;IAC5B,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;IACnB;;;;OAIG;IACH,MAAM,CAAC,EAAE,uBAAuB,CAAA;CACjC;AAED,+EAA+E;AAC/E,MAAM,WAAW,uBAAuB;IACtC,gFAAgF;IAChF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,+DAA+D;IAC/D,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACjC;AAED;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,qEAAqE;IACrE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,UAAU,CAAA;IACnC,qFAAqF;IACrF,QAAQ,CAAC,aAAa,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAA;IACzD,QAAQ,CAAC,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,aAAa,EAAE,CAAA;CAC9D;AAED,iFAAiF;AACjF,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,MAAM,CAAC,EAAE,EAAE,UAAU,gBAAgB,KAAG,CAAC,EAI/F,CAAA"}
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../../../src/lib/_internal/generators/generator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAEhC,MAAM,WAAW,aAAa;IAC5B,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;IACnB;;;;OAIG;IACH,MAAM,CAAC,EAAE,uBAAuB,CAAA;CACjC;AAED,+EAA+E;AAC/E,MAAM,WAAW,uBAAuB;IACtC,gFAAgF;IAChF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,+DAA+D;IAC/D,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACjC;AAED;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,qEAAqE;IACrE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,UAAU,CAAA;IACnC,qFAAqF;IACrF,QAAQ,CAAC,aAAa,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAA;IACzD,kFAAkF;IAClF,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAA;IAC7B,QAAQ,CAAC,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,aAAa,EAAE,CAAA;CAC9D;AAED,iFAAiF;AACjF,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,MAAM,CAAC,EAAE,EAAE,UAAU,gBAAgB,KAAG,CAAC,EAI/F,CAAA"}
@@ -2,17 +2,14 @@ import { GeneratorRegistry } from './registry.js';
2
2
  export type { GeneratedFile, Generator, GeneratorContext } from './generator.js';
3
3
  export { GeneratorRegistry } from './registry.js';
4
4
  export { createTypeScriptGenerator } from './typescript/typescript-generator.js';
5
- export { createTypeScriptSplitGenerator } from './typescript/typescript-split-generator.js';
6
5
  export type { TypeScriptGeneratorOptions } from './typescript/typescript-generator.js';
7
6
  export { createZodGenerator } from './zod/zod-generator.js';
8
- export { createZodSplitGenerator } from './zod/zod-split-generator.js';
9
- export { createDataConnectGraphqlGenerator, createDataConnectGraphqlSplitGenerator } from './data-connect/graphql-generator.js';
7
+ export { createDataConnectGraphqlGenerator } from './data-connect/graphql-generator.js';
10
8
  export { createDataConnectAdapterGenerator } from './data-connect/adapter-generator.js';
11
- export { createDataConnectOperationsGenerator, createDataConnectOperationsSplitGenerator } from './data-connect/operations-generator.js';
9
+ export { createDataConnectOperationsGenerator } from './data-connect/operations-generator.js';
12
10
  export { createFirestoreTypeGenerator } from './firestore/firestore-type-generator.js';
13
11
  export type { FirestoreGeneratorOptions } from './firestore/firestore-type-generator.js';
14
12
  export { createFirestoreProjectionGenerator } from './firestore/firestore-projection-generator.js';
15
- export { createFirestoreSplitGenerator } from './firestore/firestore-split-generator.js';
16
13
  export { createApiTypesGenerator } from './api/api-types-generator.js';
17
14
  export { createApiValidationGenerator } from './api/api-validation-generator.js';
18
15
  export { createApiDtoGenerator } from './api/api-dto-generator.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/_internal/generators/index.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAMjD,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAChF,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAA;AAChF,OAAO,EAAE,8BAA8B,EAAE,MAAM,4CAA4C,CAAA;AAC3F,YAAY,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAA;AACtF,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AACtE,OAAO,EAAE,iCAAiC,EAAE,sCAAsC,EAAE,MAAM,qCAAqC,CAAA;AAC/H,OAAO,EAAE,iCAAiC,EAAE,MAAM,qCAAqC,CAAA;AACvF,OAAO,EAAE,oCAAoC,EAAE,yCAAyC,EAAE,MAAM,wCAAwC,CAAA;AACxI,OAAO,EAAE,4BAA4B,EAAE,MAAM,yCAAyC,CAAA;AACtF,YAAY,EAAE,yBAAyB,EAAE,MAAM,yCAAyC,CAAA;AACxF,OAAO,EAAE,kCAAkC,EAAE,MAAM,+CAA+C,CAAA;AAClG,OAAO,EAAE,6BAA6B,EAAE,MAAM,0CAA0C,CAAA;AACxF,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AACtE,OAAO,EAAE,4BAA4B,EAAE,MAAM,mCAAmC,CAAA;AAChF,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AAClE,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAA;AAC5E,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAA;AAC9E,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAA;AACnE,YAAY,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAA;AACzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAA;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAA;AAEpE;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,QAAO,iBAqBH,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/_internal/generators/index.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAIjD,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAChF,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAA;AAChF,YAAY,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAA;AACtF,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,EAAE,iCAAiC,EAAE,MAAM,qCAAqC,CAAA;AACvF,OAAO,EAAE,iCAAiC,EAAE,MAAM,qCAAqC,CAAA;AACvF,OAAO,EAAE,oCAAoC,EAAE,MAAM,wCAAwC,CAAA;AAC7F,OAAO,EAAE,4BAA4B,EAAE,MAAM,yCAAyC,CAAA;AACtF,YAAY,EAAE,yBAAyB,EAAE,MAAM,yCAAyC,CAAA;AACxF,OAAO,EAAE,kCAAkC,EAAE,MAAM,+CAA+C,CAAA;AAClG,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AACtE,OAAO,EAAE,4BAA4B,EAAE,MAAM,mCAAmC,CAAA;AAChF,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AAClE,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAA;AAC5E,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAA;AAC9E,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAA;AACnE,YAAY,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAA;AACzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAA;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAA;AAEpE;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,QAAO,iBAgBH,CAAA"}
@@ -4,30 +4,24 @@ import { createApiValidationGenerator } from './api/api-validation-generator.js'
4
4
  import { createTaskPayloadGenerator } from './api/task-payload-generator.js';
5
5
  import { createConfigGenerator } from './config/config-generator.js';
6
6
  import { createDataConnectAdapterGenerator } from './data-connect/adapter-generator.js';
7
- import { createDataConnectGraphqlGenerator, createDataConnectGraphqlSplitGenerator } from './data-connect/graphql-generator.js';
8
- import { createDataConnectOperationsGenerator, createDataConnectOperationsSplitGenerator } from './data-connect/operations-generator.js';
7
+ import { createDataConnectGraphqlGenerator } from './data-connect/graphql-generator.js';
8
+ import { createDataConnectOperationsGenerator } from './data-connect/operations-generator.js';
9
9
  import { createFirestoreProjectionGenerator } from './firestore/firestore-projection-generator.js';
10
- import { createFirestoreSplitGenerator } from './firestore/firestore-split-generator.js';
11
10
  import { createFirestoreTypeGenerator } from './firestore/firestore-type-generator.js';
12
11
  import { createIdCodecGenerator } from './id/id-codec-generator.js';
13
12
  import { createSqlMigrationGenerator } from './sql/sql-migration-generator.js';
14
13
  import { createUnionGenerator } from './union/union-generator.js';
15
14
  import { GeneratorRegistry } from './registry.js';
16
15
  import { createTypeScriptGenerator } from './typescript/typescript-generator.js';
17
- import { createTypeScriptSplitGenerator } from './typescript/typescript-split-generator.js';
18
16
  import { createZodGenerator } from './zod/zod-generator.js';
19
- import { createZodSplitGenerator } from './zod/zod-split-generator.js';
20
17
  export { GeneratorRegistry } from './registry.js';
21
18
  export { createTypeScriptGenerator } from './typescript/typescript-generator.js';
22
- export { createTypeScriptSplitGenerator } from './typescript/typescript-split-generator.js';
23
19
  export { createZodGenerator } from './zod/zod-generator.js';
24
- export { createZodSplitGenerator } from './zod/zod-split-generator.js';
25
- export { createDataConnectGraphqlGenerator, createDataConnectGraphqlSplitGenerator } from './data-connect/graphql-generator.js';
20
+ export { createDataConnectGraphqlGenerator } from './data-connect/graphql-generator.js';
26
21
  export { createDataConnectAdapterGenerator } from './data-connect/adapter-generator.js';
27
- export { createDataConnectOperationsGenerator, createDataConnectOperationsSplitGenerator } from './data-connect/operations-generator.js';
22
+ export { createDataConnectOperationsGenerator } from './data-connect/operations-generator.js';
28
23
  export { createFirestoreTypeGenerator } from './firestore/firestore-type-generator.js';
29
24
  export { createFirestoreProjectionGenerator } from './firestore/firestore-projection-generator.js';
30
- export { createFirestoreSplitGenerator } from './firestore/firestore-split-generator.js';
31
25
  export { createApiTypesGenerator } from './api/api-types-generator.js';
32
26
  export { createApiValidationGenerator } from './api/api-validation-generator.js';
33
27
  export { createApiDtoGenerator } from './api/api-dto-generator.js';
@@ -43,17 +37,12 @@ export { createConfigGenerator } from './config/config-generator.js';
43
37
  */
44
38
  export const createDefaultRegistry = () => new GeneratorRegistry()
45
39
  .register(createTypeScriptGenerator())
46
- .register(createTypeScriptSplitGenerator())
47
40
  .register(createZodGenerator())
48
- .register(createZodSplitGenerator())
49
41
  .register(createDataConnectGraphqlGenerator())
50
- .register(createDataConnectGraphqlSplitGenerator())
51
42
  .register(createDataConnectOperationsGenerator())
52
- .register(createDataConnectOperationsSplitGenerator())
53
43
  .register(createDataConnectAdapterGenerator())
54
44
  .register(createFirestoreTypeGenerator())
55
45
  .register(createFirestoreProjectionGenerator())
56
- .register(createFirestoreSplitGenerator())
57
46
  .register(createApiTypesGenerator())
58
47
  .register(createApiValidationGenerator())
59
48
  .register(createApiDtoGenerator())
@@ -3,7 +3,8 @@ import { Generator } from '../generator.js';
3
3
  * Generates raw SQL migrations for constraints Data Connect cannot express:
4
4
  * composite foreign keys, CHECK constraints, and extra indexes — constraints
5
5
  * that otherwise live only as hand-written schema comments with no executable
6
- * artifact. This turns them into a runnable `.sql` file.
6
+ * artifact. Every statement is idempotent (`IF NOT EXISTS` / duplicate_object
7
+ * guard), so the whole file can be (re)applied to any environment safely.
7
8
  */
8
9
  export declare const createSqlMigrationGenerator: () => Generator;
9
10
  //# sourceMappingURL=sql-migration-generator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sql-migration-generator.d.ts","sourceRoot":"","sources":["../../../../../src/lib/_internal/generators/sql/sql-migration-generator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiB,SAAS,EAAoB,MAAM,iBAAiB,CAAA;AAgC5E;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,QAAO,SAY7C,CAAA"}
1
+ {"version":3,"file":"sql-migration-generator.d.ts","sourceRoot":"","sources":["../../../../../src/lib/_internal/generators/sql/sql-migration-generator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiB,SAAS,EAAoB,MAAM,iBAAiB,CAAA;AA0C5E;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,QAAO,SAY7C,CAAA"}
@@ -2,6 +2,12 @@ import { pluralize, snakeCase } from '../support/naming.js';
2
2
  import { headerBlocks } from '../support/header.js';
3
3
  import { outputFile } from '../support/templates.js';
4
4
  const tableName = (model) => model.table ?? pluralize(snakeCase(model.name));
5
+ /**
6
+ * Postgres has no `ADD CONSTRAINT IF NOT EXISTS`, so constraint statements are
7
+ * wrapped in a DO block that swallows `duplicate_object` — rerunning the whole
8
+ * file against an already-migrated database is a no-op.
9
+ */
10
+ const guarded = (statement) => ['DO $$ BEGIN', ` ${statement}`, 'EXCEPTION WHEN duplicate_object THEN NULL;', 'END $$;'].join('\n');
5
11
  const renderModelSql = (model) => {
6
12
  const sql = model.sql;
7
13
  if (!sql)
@@ -10,17 +16,17 @@ const renderModelSql = (model) => {
10
16
  const statements = [];
11
17
  sql.checks.forEach((expr, i) => {
12
18
  const name = `${table}_chk_${i + 1}`;
13
- statements.push(`ALTER TABLE "${table}" ADD CONSTRAINT "${name}" CHECK (${expr});`);
19
+ statements.push(guarded(`ALTER TABLE "${table}" ADD CONSTRAINT "${name}" CHECK (${expr});`));
14
20
  });
15
21
  for (const fk of sql.foreignKeys) {
16
22
  const name = fk.name ?? `${table}_${fk.columns.join('_')}_fkey`;
17
23
  const cols = fk.columns.map(c => `"${c}"`).join(', ');
18
- statements.push(`ALTER TABLE "${table}" ADD CONSTRAINT "${name}" FOREIGN KEY (${cols}) REFERENCES ${fk.references};`);
24
+ statements.push(guarded(`ALTER TABLE "${table}" ADD CONSTRAINT "${name}" FOREIGN KEY (${cols}) REFERENCES ${fk.references};`));
19
25
  }
20
26
  for (const idx of sql.indexes) {
21
27
  const name = idx.name ?? `${table}_${idx.columns.join('_')}_${idx.unique ? 'uidx' : 'idx'}`;
22
28
  const cols = idx.columns.map(c => `"${c}"`).join(', ');
23
- statements.push(`CREATE ${idx.unique ? 'UNIQUE ' : ''}INDEX "${name}" ON "${table}" (${cols});`);
29
+ statements.push(`CREATE ${idx.unique ? 'UNIQUE ' : ''}INDEX IF NOT EXISTS "${name}" ON "${table}" (${cols});`);
24
30
  }
25
31
  return statements.length > 0 ? [`-- ${model.name} (${table})`, ...statements] : [];
26
32
  };
@@ -28,7 +34,8 @@ const renderModelSql = (model) => {
28
34
  * Generates raw SQL migrations for constraints Data Connect cannot express:
29
35
  * composite foreign keys, CHECK constraints, and extra indexes — constraints
30
36
  * that otherwise live only as hand-written schema comments with no executable
31
- * artifact. This turns them into a runnable `.sql` file.
37
+ * artifact. Every statement is idempotent (`IF NOT EXISTS` / duplicate_object
38
+ * guard), so the whole file can be (re)applied to any environment safely.
32
39
  */
33
40
  export const createSqlMigrationGenerator = () => ({
34
41
  name: 'sql-migrations',
@@ -0,0 +1,3 @@
1
+ /** Render a JSDoc block for a possibly multi-line description ('' when absent). */
2
+ export declare const jsdoc: (description: string | undefined, indent: string) => string;
3
+ //# sourceMappingURL=jsdoc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsdoc.d.ts","sourceRoot":"","sources":["../../../../../src/lib/_internal/generators/support/jsdoc.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,eAAO,MAAM,KAAK,GAAI,aAAa,MAAM,GAAG,SAAS,EAAE,QAAQ,MAAM,KAAG,MAKvE,CAAA"}
@@ -0,0 +1,9 @@
1
+ /** Render a JSDoc block for a possibly multi-line description ('' when absent). */
2
+ export const jsdoc = (description, indent) => {
3
+ if (!description)
4
+ return '';
5
+ const lines = description.split('\n');
6
+ if (lines.length === 1)
7
+ return `${indent}/** ${lines[0]} */\n`;
8
+ return `${indent}/**\n${lines.map(line => `${indent} * ${line}`.trimEnd()).join('\n')}\n${indent} */\n`;
9
+ };
@@ -0,0 +1,9 @@
1
+ import { Generator } from '../generator.js';
2
+ /**
3
+ * Merge a single-file generator and its per-item split layout into ONE
4
+ * registry entry: `split: true` in the declaration routes to the split
5
+ * implementation, everything else (name, description, defaults) comes from
6
+ * the base. Keeps "one concern = one generator id" in the YAML surface.
7
+ */
8
+ export declare const withSplitVariant: (base: Generator, splitVariant: Generator) => Generator;
9
+ //# sourceMappingURL=split-variant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"split-variant.d.ts","sourceRoot":"","sources":["../../../../../src/lib/_internal/generators/support/split-variant.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3C;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,SAAS,EAAE,cAAc,SAAS,KAAG,SAM1E,CAAA"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Merge a single-file generator and its per-item split layout into ONE
3
+ * registry entry: `split: true` in the declaration routes to the split
4
+ * implementation, everything else (name, description, defaults) comes from
5
+ * the base. Keeps "one concern = one generator id" in the YAML surface.
6
+ */
7
+ export const withSplitVariant = (base, splitVariant) => ({
8
+ ...base,
9
+ splittable: true,
10
+ generate(ir, context) {
11
+ return (context?.output?.split ? splitVariant : base).generate(ir, context);
12
+ },
13
+ });
@@ -18,7 +18,7 @@ export declare const collectDeps: (ir: Ir, roots: IrModel[]) => HostedDeps;
18
18
  /**
19
19
  * Assign a unique module basename to every table. Two models may share a
20
20
  * physical table name across services (e.g. both declare
21
- * `reconciliation_operations`); the first keeps the table-derived name and
21
+ * `audit_logs`); the first keeps the table-derived name and
22
22
  * later ones fall back to their model name so files never overwrite each other.
23
23
  */
24
24
  export declare const assignModuleNames: (tables: IrModel[]) => Map<string, string>;
@@ -1 +1 @@
1
- {"version":3,"file":"split.d.ts","sourceRoot":"","sources":["../../../../../src/lib/_internal/generators/support/split.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,EAAE,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAIvD,qEAAqE;AACrE,eAAO,MAAM,OAAO,GAAI,OAAO,OAAO,KAAG,OAAiE,CAAA;AAE1G,eAAO,MAAM,WAAW,GAAI,OAAO,OAAO,KAAG,MAAyD,CAAA;AAEtG,eAAO,MAAM,SAAS,GAAI,OAAO,MAAM,KAAG,MAA6C,CAAA;AAavF,MAAM,WAAW,UAAU;IACzB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,8DAA8D;IAC9D,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB;AAED;;;;GAIG;AACH,eAAO,MAAM,WAAW,GAAI,IAAI,EAAE,EAAE,OAAO,OAAO,EAAE,KAAG,UAuBtD,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAAI,QAAQ,OAAO,EAAE,KAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAavE,CAAA;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,EAAE,CAAA;IACjB,2CAA2C;IAC3C,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,qDAAqD;IACrD,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,oDAAoD;IACpD,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,8CAA8C;IAC9C,cAAc,EAAE,MAAM,EAAE,CAAA;CACzB;AAED;;;GAGG;AACH,eAAO,MAAM,WAAW,GAAI,IAAI,EAAE,KAAG,WAepC,CAAA"}
1
+ {"version":3,"file":"split.d.ts","sourceRoot":"","sources":["../../../../../src/lib/_internal/generators/support/split.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,EAAE,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAIvD,qEAAqE;AACrE,eAAO,MAAM,OAAO,GAAI,OAAO,OAAO,KAAG,OAAiE,CAAA;AAE1G,eAAO,MAAM,WAAW,GAAI,OAAO,OAAO,KAAG,MAAyD,CAAA;AAEtG,eAAO,MAAM,SAAS,GAAI,OAAO,MAAM,KAAG,MAA6C,CAAA;AAavF,MAAM,WAAW,UAAU;IACzB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,8DAA8D;IAC9D,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB;AAED;;;;GAIG;AACH,eAAO,MAAM,WAAW,GAAI,IAAI,EAAE,EAAE,OAAO,OAAO,EAAE,KAAG,UAuBtD,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAAI,QAAQ,OAAO,EAAE,KAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAavE,CAAA;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,EAAE,CAAA;IACjB,2CAA2C;IAC3C,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,qDAAqD;IACrD,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,oDAAoD;IACpD,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,8CAA8C;IAC9C,cAAc,EAAE,MAAM,EAAE,CAAA;CACzB;AAED;;;GAGG;AACH,eAAO,MAAM,WAAW,GAAI,IAAI,EAAE,KAAG,WAgBpC,CAAA"}