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.
- package/README.md +141 -64
- package/dist/lib/_internal/compiler/compiler.d.ts.map +1 -1
- package/dist/lib/_internal/compiler/compiler.js +50 -21
- package/dist/lib/_internal/generators/api/api-dto-generator.d.ts.map +1 -1
- package/dist/lib/_internal/generators/api/api-dto-generator.js +1 -8
- package/dist/lib/_internal/generators/api/api-types-generator.d.ts.map +1 -1
- package/dist/lib/_internal/generators/api/api-types-generator.js +11 -5
- package/dist/lib/_internal/generators/api/task-payload-generator.js +3 -3
- package/dist/lib/_internal/generators/data-connect/graphql-generator.d.ts +0 -7
- package/dist/lib/_internal/generators/data-connect/graphql-generator.d.ts.map +1 -1
- package/dist/lib/_internal/generators/data-connect/graphql-generator.js +5 -4
- package/dist/lib/_internal/generators/data-connect/operations-generator.d.ts +0 -7
- package/dist/lib/_internal/generators/data-connect/operations-generator.d.ts.map +1 -1
- package/dist/lib/_internal/generators/data-connect/operations-generator.js +8 -7
- package/dist/lib/_internal/generators/firestore/firestore-projection-generator.d.ts.map +1 -1
- package/dist/lib/_internal/generators/firestore/firestore-projection-generator.js +7 -22
- package/dist/lib/_internal/generators/firestore/firestore-split-generator.d.ts +1 -1
- package/dist/lib/_internal/generators/firestore/firestore-split-generator.d.ts.map +1 -1
- package/dist/lib/_internal/generators/firestore/firestore-split-generator.js +69 -47
- package/dist/lib/_internal/generators/generator.d.ts +2 -0
- package/dist/lib/_internal/generators/generator.d.ts.map +1 -1
- package/dist/lib/_internal/generators/index.d.ts +2 -5
- package/dist/lib/_internal/generators/index.d.ts.map +1 -1
- package/dist/lib/_internal/generators/index.js +4 -15
- package/dist/lib/_internal/generators/sql/sql-migration-generator.d.ts +2 -1
- package/dist/lib/_internal/generators/sql/sql-migration-generator.d.ts.map +1 -1
- package/dist/lib/_internal/generators/sql/sql-migration-generator.js +11 -4
- package/dist/lib/_internal/generators/support/jsdoc.d.ts +3 -0
- package/dist/lib/_internal/generators/support/jsdoc.d.ts.map +1 -0
- package/dist/lib/_internal/generators/support/jsdoc.js +9 -0
- package/dist/lib/_internal/generators/support/split-variant.d.ts +9 -0
- package/dist/lib/_internal/generators/support/split-variant.d.ts.map +1 -0
- package/dist/lib/_internal/generators/support/split-variant.js +13 -0
- package/dist/lib/_internal/generators/support/split.d.ts +1 -1
- package/dist/lib/_internal/generators/support/split.d.ts.map +1 -1
- package/dist/lib/_internal/generators/support/split.js +3 -2
- package/dist/lib/_internal/generators/typescript/render.d.ts +15 -0
- package/dist/lib/_internal/generators/typescript/render.d.ts.map +1 -0
- package/dist/lib/_internal/generators/typescript/render.js +92 -0
- package/dist/lib/_internal/generators/typescript/typescript-generator.d.ts +2 -1
- package/dist/lib/_internal/generators/typescript/typescript-generator.d.ts.map +1 -1
- package/dist/lib/_internal/generators/typescript/typescript-generator.js +12 -78
- package/dist/lib/_internal/generators/typescript/typescript-split-generator.d.ts +1 -1
- package/dist/lib/_internal/generators/typescript/typescript-split-generator.d.ts.map +1 -1
- package/dist/lib/_internal/generators/typescript/typescript-split-generator.js +4 -84
- package/dist/lib/_internal/generators/zod/render.d.ts +12 -0
- package/dist/lib/_internal/generators/zod/render.d.ts.map +1 -0
- package/dist/lib/_internal/generators/zod/render.js +59 -0
- package/dist/lib/_internal/generators/zod/zod-generator.d.ts +2 -1
- package/dist/lib/_internal/generators/zod/zod-generator.d.ts.map +1 -1
- package/dist/lib/_internal/generators/zod/zod-generator.js +11 -55
- package/dist/lib/_internal/generators/zod/zod-split-generator.d.ts +1 -1
- package/dist/lib/_internal/generators/zod/zod-split-generator.d.ts.map +1 -1
- package/dist/lib/_internal/generators/zod/zod-split-generator.js +4 -56
- package/dist/lib/_internal/ir/build-ir.d.ts.map +1 -1
- package/dist/lib/_internal/ir/build-ir.js +93 -8
- package/dist/lib/_internal/ir/ir.d.ts +18 -5
- package/dist/lib/_internal/ir/ir.d.ts.map +1 -1
- package/dist/lib/_internal/ir/ir.js +1 -0
- package/dist/lib/_internal/parser/parse.d.ts +5 -3
- package/dist/lib/_internal/parser/parse.d.ts.map +1 -1
- package/dist/lib/_internal/parser/parse.js +103 -14
- package/dist/lib/_internal/parser/raw-document.d.ts +18 -2
- package/dist/lib/_internal/parser/raw-document.d.ts.map +1 -1
- package/dist/lib/_internal/resolver/import-resolver.js +1 -1
- package/dist/lib/_internal/validation/rules.d.ts +39 -0
- package/dist/lib/_internal/validation/rules.d.ts.map +1 -1
- package/dist/lib/_internal/validation/rules.js +133 -1
- package/dist/lib/cli/commands/generate.d.ts.map +1 -1
- package/dist/lib/cli/commands/generate.js +6 -4
- package/dist/lib/cli/commands/index.d.ts.map +1 -1
- package/dist/lib/cli/commands/index.js +2 -0
- package/dist/lib/cli/commands/init.d.ts.map +1 -1
- package/dist/lib/cli/commands/init.js +2 -18
- package/dist/lib/cli/commands/where.d.ts +3 -0
- package/dist/lib/cli/commands/where.d.ts.map +1 -0
- package/dist/lib/cli/commands/where.js +57 -0
- package/dist/lib/cli/config.d.ts +3 -2
- package/dist/lib/cli/config.d.ts.map +1 -1
- package/dist/lib/cli/config.js +3 -0
- 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
|
-
|
|
175
|
-
name: 'data-connect-graphql
|
|
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;
|
|
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
|
|
501
|
+
* Split layout matching the real connector repo structure:
|
|
501
502
|
* `<connector>/operations/<entity>/{queries,mutations}.gql` (+ one
|
|
502
|
-
* `operations-types.ts` per connector).
|
|
503
|
-
* `data-connect-operations`
|
|
503
|
+
* `operations-types.ts` per connector). Selected via `split: true` on the
|
|
504
|
+
* `data-connect-operations` declaration.
|
|
504
505
|
*/
|
|
505
|
-
|
|
506
|
-
name: 'data-connect-operations
|
|
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;
|
|
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
|
-
|
|
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
|
|
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;
|
|
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
|
|
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
|
|
237
|
-
name: 'firestore
|
|
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
|
-
|
|
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
|
-
|
|
289
|
-
|
|
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
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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
|
-
|
|
371
|
-
exportLines.push(`export * from '
|
|
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 {
|
|
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
|
|
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":"
|
|
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
|
|
8
|
-
import { createDataConnectOperationsGenerator
|
|
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 {
|
|
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
|
|
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.
|
|
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;
|
|
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.
|
|
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 @@
|
|
|
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
|
-
* `
|
|
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,
|
|
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"}
|