firebase-contract 0.1.0-rc.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 +560 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/lib/_internal/compiler/compiler.d.ts +71 -0
- package/dist/lib/_internal/compiler/compiler.d.ts.map +1 -0
- package/dist/lib/_internal/compiler/compiler.js +104 -0
- package/dist/lib/_internal/compiler/index.d.ts +3 -0
- package/dist/lib/_internal/compiler/index.d.ts.map +1 -0
- package/dist/lib/_internal/compiler/index.js +1 -0
- package/dist/lib/_internal/diagnostics.d.ts +29 -0
- package/dist/lib/_internal/diagnostics.d.ts.map +1 -0
- package/dist/lib/_internal/diagnostics.js +26 -0
- package/dist/lib/_internal/format-diagnostics.d.ts +8 -0
- package/dist/lib/_internal/format-diagnostics.d.ts.map +1 -0
- package/dist/lib/_internal/format-diagnostics.js +10 -0
- package/dist/lib/_internal/generators/api/api-dto-generator.d.ts +8 -0
- package/dist/lib/_internal/generators/api/api-dto-generator.d.ts.map +1 -0
- package/dist/lib/_internal/generators/api/api-dto-generator.js +134 -0
- package/dist/lib/_internal/generators/api/api-types-generator.d.ts +8 -0
- package/dist/lib/_internal/generators/api/api-types-generator.d.ts.map +1 -0
- package/dist/lib/_internal/generators/api/api-types-generator.js +105 -0
- package/dist/lib/_internal/generators/api/api-validation-generator.d.ts +7 -0
- package/dist/lib/_internal/generators/api/api-validation-generator.d.ts.map +1 -0
- package/dist/lib/_internal/generators/api/api-validation-generator.js +73 -0
- package/dist/lib/_internal/generators/api/task-payload-generator.d.ts +9 -0
- package/dist/lib/_internal/generators/api/task-payload-generator.d.ts.map +1 -0
- package/dist/lib/_internal/generators/api/task-payload-generator.js +96 -0
- package/dist/lib/_internal/generators/config/config-generator.d.ts +9 -0
- package/dist/lib/_internal/generators/config/config-generator.d.ts.map +1 -0
- package/dist/lib/_internal/generators/config/config-generator.js +71 -0
- package/dist/lib/_internal/generators/data-connect/adapter-generator.d.ts +8 -0
- package/dist/lib/_internal/generators/data-connect/adapter-generator.d.ts.map +1 -0
- package/dist/lib/_internal/generators/data-connect/adapter-generator.js +142 -0
- package/dist/lib/_internal/generators/data-connect/graphql-generator.d.ts +15 -0
- package/dist/lib/_internal/generators/data-connect/graphql-generator.d.ts.map +1 -0
- package/dist/lib/_internal/generators/data-connect/graphql-generator.js +209 -0
- package/dist/lib/_internal/generators/data-connect/operations-generator.d.ts +18 -0
- package/dist/lib/_internal/generators/data-connect/operations-generator.d.ts.map +1 -0
- package/dist/lib/_internal/generators/data-connect/operations-generator.js +539 -0
- package/dist/lib/_internal/generators/firestore/firestore-projection-generator.d.ts +11 -0
- package/dist/lib/_internal/generators/firestore/firestore-projection-generator.d.ts.map +1 -0
- package/dist/lib/_internal/generators/firestore/firestore-projection-generator.js +112 -0
- package/dist/lib/_internal/generators/firestore/firestore-split-generator.d.ts +11 -0
- package/dist/lib/_internal/generators/firestore/firestore-split-generator.d.ts.map +1 -0
- package/dist/lib/_internal/generators/firestore/firestore-split-generator.js +375 -0
- package/dist/lib/_internal/generators/firestore/firestore-type-generator.d.ts +12 -0
- package/dist/lib/_internal/generators/firestore/firestore-type-generator.d.ts.map +1 -0
- package/dist/lib/_internal/generators/firestore/firestore-type-generator.js +81 -0
- package/dist/lib/_internal/generators/generator.d.ts +23 -0
- package/dist/lib/_internal/generators/generator.d.ts.map +1 -0
- package/dist/lib/_internal/generators/generator.js +1 -0
- package/dist/lib/_internal/generators/id/id-codec-generator.d.ts +16 -0
- package/dist/lib/_internal/generators/id/id-codec-generator.d.ts.map +1 -0
- package/dist/lib/_internal/generators/id/id-codec-generator.js +140 -0
- package/dist/lib/_internal/generators/index.d.ts +31 -0
- package/dist/lib/_internal/generators/index.d.ts.map +1 -0
- package/dist/lib/_internal/generators/index.js +64 -0
- package/dist/lib/_internal/generators/registry.d.ts +15 -0
- package/dist/lib/_internal/generators/registry.d.ts.map +1 -0
- package/dist/lib/_internal/generators/registry.js +24 -0
- package/dist/lib/_internal/generators/sql/sql-migration-generator.d.ts +9 -0
- package/dist/lib/_internal/generators/sql/sql-migration-generator.d.ts.map +1 -0
- package/dist/lib/_internal/generators/sql/sql-migration-generator.js +44 -0
- package/dist/lib/_internal/generators/support/constraints.d.ts +8 -0
- package/dist/lib/_internal/generators/support/constraints.d.ts.map +1 -0
- package/dist/lib/_internal/generators/support/constraints.js +39 -0
- package/dist/lib/_internal/generators/support/header.d.ts +13 -0
- package/dist/lib/_internal/generators/support/header.d.ts.map +1 -0
- package/dist/lib/_internal/generators/support/header.js +17 -0
- package/dist/lib/_internal/generators/support/naming.d.ts +10 -0
- package/dist/lib/_internal/generators/support/naming.d.ts.map +1 -0
- package/dist/lib/_internal/generators/support/naming.js +30 -0
- package/dist/lib/_internal/generators/support/relations.d.ts +12 -0
- package/dist/lib/_internal/generators/support/relations.d.ts.map +1 -0
- package/dist/lib/_internal/generators/support/relations.js +11 -0
- package/dist/lib/_internal/generators/support/split.d.ts +34 -0
- package/dist/lib/_internal/generators/support/split.d.ts.map +1 -0
- package/dist/lib/_internal/generators/support/split.js +75 -0
- package/dist/lib/_internal/generators/typescript/typescript-generator.d.ts +18 -0
- package/dist/lib/_internal/generators/typescript/typescript-generator.d.ts.map +1 -0
- package/dist/lib/_internal/generators/typescript/typescript-generator.js +99 -0
- package/dist/lib/_internal/generators/typescript/typescript-split-generator.d.ts +10 -0
- package/dist/lib/_internal/generators/typescript/typescript-split-generator.d.ts.map +1 -0
- package/dist/lib/_internal/generators/typescript/typescript-split-generator.js +175 -0
- package/dist/lib/_internal/generators/union/union-generator.d.ts +8 -0
- package/dist/lib/_internal/generators/union/union-generator.d.ts.map +1 -0
- package/dist/lib/_internal/generators/union/union-generator.js +37 -0
- package/dist/lib/_internal/generators/zod/zod-generator.d.ts +7 -0
- package/dist/lib/_internal/generators/zod/zod-generator.d.ts.map +1 -0
- package/dist/lib/_internal/generators/zod/zod-generator.js +68 -0
- package/dist/lib/_internal/generators/zod/zod-split-generator.d.ts +9 -0
- package/dist/lib/_internal/generators/zod/zod-split-generator.d.ts.map +1 -0
- package/dist/lib/_internal/generators/zod/zod-split-generator.js +136 -0
- package/dist/lib/_internal/ir/build-ir.d.ts +18 -0
- package/dist/lib/_internal/ir/build-ir.d.ts.map +1 -0
- package/dist/lib/_internal/ir/build-ir.js +430 -0
- package/dist/lib/_internal/ir/ir.d.ts +393 -0
- package/dist/lib/_internal/ir/ir.d.ts.map +1 -0
- package/dist/lib/_internal/ir/ir.js +22 -0
- package/dist/lib/_internal/parser/parse.d.ts +9 -0
- package/dist/lib/_internal/parser/parse.d.ts.map +1 -0
- package/dist/lib/_internal/parser/parse.js +682 -0
- package/dist/lib/_internal/parser/raw-document.d.ts +230 -0
- package/dist/lib/_internal/parser/raw-document.d.ts.map +1 -0
- package/dist/lib/_internal/parser/raw-document.js +6 -0
- package/dist/lib/_internal/resolver/import-resolver.d.ts +25 -0
- package/dist/lib/_internal/resolver/import-resolver.d.ts.map +1 -0
- package/dist/lib/_internal/resolver/import-resolver.js +73 -0
- package/dist/lib/_internal/resolver/module-loader.d.ts +25 -0
- package/dist/lib/_internal/resolver/module-loader.d.ts.map +1 -0
- package/dist/lib/_internal/resolver/module-loader.js +62 -0
- package/dist/lib/_internal/runtime/data-connect-adapter.d.ts +25 -0
- package/dist/lib/_internal/runtime/data-connect-adapter.d.ts.map +1 -0
- package/dist/lib/_internal/runtime/data-connect-adapter.js +24 -0
- package/dist/lib/_internal/runtime/index.d.ts +3 -0
- package/dist/lib/_internal/runtime/index.d.ts.map +1 -0
- package/dist/lib/_internal/runtime/index.js +1 -0
- package/dist/lib/_internal/validation/rules.d.ts +32 -0
- package/dist/lib/_internal/validation/rules.d.ts.map +1 -0
- package/dist/lib/_internal/validation/rules.js +282 -0
- package/dist/lib/_internal/validation/validate.d.ts +9 -0
- package/dist/lib/_internal/validation/validate.d.ts.map +1 -0
- package/dist/lib/_internal/validation/validate.js +6 -0
- package/dist/lib/cli/commands/generate.d.ts +3 -0
- package/dist/lib/cli/commands/generate.d.ts.map +1 -0
- package/dist/lib/cli/commands/generate.js +66 -0
- package/dist/lib/cli/commands/index.d.ts +4 -0
- package/dist/lib/cli/commands/index.d.ts.map +1 -0
- package/dist/lib/cli/commands/index.js +11 -0
- package/dist/lib/cli/commands/init.d.ts +3 -0
- package/dist/lib/cli/commands/init.d.ts.map +1 -0
- package/dist/lib/cli/commands/init.js +72 -0
- package/dist/lib/cli/commands/inspect.d.ts +3 -0
- package/dist/lib/cli/commands/inspect.d.ts.map +1 -0
- package/dist/lib/cli/commands/inspect.js +23 -0
- package/dist/lib/cli/commands/validate.d.ts +3 -0
- package/dist/lib/cli/commands/validate.d.ts.map +1 -0
- package/dist/lib/cli/commands/validate.js +23 -0
- package/dist/lib/cli/config.d.ts +12 -0
- package/dist/lib/cli/config.d.ts.map +1 -0
- package/dist/lib/cli/config.js +20 -0
- package/dist/lib/cli/index.d.ts +3 -0
- package/dist/lib/cli/index.d.ts.map +1 -0
- package/dist/lib/cli/index.js +13 -0
- package/dist/lib/cli/version.d.ts +3 -0
- package/dist/lib/cli/version.d.ts.map +1 -0
- package/dist/lib/cli/version.js +15 -0
- package/dist/lib/modules/runtime.d.ts +2 -0
- package/dist/lib/modules/runtime.d.ts.map +1 -0
- package/dist/lib/modules/runtime.js +3 -0
- package/package.json +60 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { findModel } from '../../ir/ir.js';
|
|
2
|
+
import { constantCase, pascalCase, singleQuote } from '../support/naming.js';
|
|
3
|
+
import { isRelation, relationFkName, relationFkType } from '../support/relations.js';
|
|
4
|
+
import { headerBlocks } from '../support/header.js';
|
|
5
|
+
const SCALAR_TS = {
|
|
6
|
+
string: 'string',
|
|
7
|
+
int: 'number',
|
|
8
|
+
int64: 'number',
|
|
9
|
+
float: 'number',
|
|
10
|
+
boolean: 'boolean',
|
|
11
|
+
timestamp: 'string',
|
|
12
|
+
date: 'string',
|
|
13
|
+
json: 'unknown',
|
|
14
|
+
id: 'string',
|
|
15
|
+
};
|
|
16
|
+
const isTask = (api) => api.kind === 'task' || api.kind === 'pubsub';
|
|
17
|
+
const fieldTs = (ir, field) => {
|
|
18
|
+
let base;
|
|
19
|
+
if (isRelation(field))
|
|
20
|
+
base = SCALAR_TS[relationFkType(ir, field)];
|
|
21
|
+
else if (field.type.kind === 'scalar')
|
|
22
|
+
base = SCALAR_TS[field.type.name];
|
|
23
|
+
else if (field.type.kind === 'enum' || field.type.kind === 'model')
|
|
24
|
+
base = field.type.name;
|
|
25
|
+
else
|
|
26
|
+
base = 'unknown';
|
|
27
|
+
return field.list ? `${base}[]` : base;
|
|
28
|
+
};
|
|
29
|
+
const renderMember = (ir, field) => {
|
|
30
|
+
const name = isRelation(field) ? relationFkName(field) : field.name;
|
|
31
|
+
return ` ${name}${field.optional ? '?' : ''}: ${fieldTs(ir, field)}`;
|
|
32
|
+
};
|
|
33
|
+
const payloadFields = (ir, payload) => payload.model ? (findModel(ir, payload.model)?.fields ?? []) : payload.fields;
|
|
34
|
+
const renderEnvelope = (env) => {
|
|
35
|
+
const doc = env.description ? `/** ${env.description} */\n` : '';
|
|
36
|
+
const members = env.fields.map(field => ` ${field.name}${field.optional ? '?' : ''}: ${SCALAR_TS[field.type.kind === 'scalar' ? field.type.name : 'string']}`).join('\n');
|
|
37
|
+
return `${doc}export type ${env.name}<T> = T & {\n${members}\n}`;
|
|
38
|
+
};
|
|
39
|
+
const renderTask = (ir, api) => {
|
|
40
|
+
const name = pascalCase(api.name);
|
|
41
|
+
const fields = payloadFields(ir, api.request);
|
|
42
|
+
const dataBody = fields.length > 0 ? fields.map(field => renderMember(ir, field)).join('\n') : '';
|
|
43
|
+
const dataType = fields.length > 0 ? `export interface ${name}TaskData {\n${dataBody}\n}` : `export type ${name}TaskData = Record<string, never>`;
|
|
44
|
+
const envelope = api.task?.envelope;
|
|
45
|
+
const payloadType = envelope
|
|
46
|
+
? `export type ${name}TaskPayload = ${envelope}<${name}TaskData>`
|
|
47
|
+
: `export type ${name}TaskPayload = ${name}TaskData`;
|
|
48
|
+
const constants = [];
|
|
49
|
+
const CONST = constantCase(api.name);
|
|
50
|
+
if (api.task?.maxAttempts !== undefined)
|
|
51
|
+
constants.push(`export const ${CONST}_MAX_ATTEMPTS = ${api.task.maxAttempts}`);
|
|
52
|
+
if (api.task?.timeoutSeconds !== undefined)
|
|
53
|
+
constants.push(`export const ${CONST}_TIMEOUT_SECONDS = ${api.task.timeoutSeconds}`);
|
|
54
|
+
if (api.task?.topic !== undefined)
|
|
55
|
+
constants.push(`export const ${CONST}_TOPIC = ${singleQuote(api.task.topic)}`);
|
|
56
|
+
return [dataType, payloadType, ...constants].join('\n');
|
|
57
|
+
};
|
|
58
|
+
const collectImports = (ir, tasks) => {
|
|
59
|
+
const names = new Set();
|
|
60
|
+
for (const api of tasks) {
|
|
61
|
+
for (const field of payloadFields(ir, api.request)) {
|
|
62
|
+
if (field.type.kind === 'enum' || (field.type.kind === 'model' && !isRelation(field))) {
|
|
63
|
+
names.add(field.type.name);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return [...names].sort();
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Generates Cloud Task / Pub/Sub payload contracts: a generic envelope type per
|
|
71
|
+
* declared envelope (`RetryTaskPayload<T> = T & { … }`), plus per-task
|
|
72
|
+
* `<Name>TaskData` + `<Name>TaskPayload` and delivery constants (max attempts,
|
|
73
|
+
* timeout, topic) — the shared payload contracts an app otherwise hand-writes.
|
|
74
|
+
*/
|
|
75
|
+
export const createTaskPayloadGenerator = () => ({
|
|
76
|
+
name: 'task-payloads',
|
|
77
|
+
description: 'Cloud Task/Pub-Sub payload envelopes, data/payload types, and constants',
|
|
78
|
+
generate(ir, context) {
|
|
79
|
+
const tasks = ir.apis.filter(isTask);
|
|
80
|
+
if (ir.envelopes.length === 0 && tasks.length === 0) {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
const blocks = [...headerBlocks(context)];
|
|
84
|
+
const imports = collectImports(ir, tasks);
|
|
85
|
+
if (imports.length > 0) {
|
|
86
|
+
blocks.push(`import type { ${imports.join(', ')} } from './types.js'`);
|
|
87
|
+
}
|
|
88
|
+
for (const env of ir.envelopes) {
|
|
89
|
+
blocks.push(renderEnvelope(env));
|
|
90
|
+
}
|
|
91
|
+
for (const api of tasks) {
|
|
92
|
+
blocks.push(renderTask(ir, api));
|
|
93
|
+
}
|
|
94
|
+
return [{ path: 'task-payloads.ts', content: `${blocks.join('\n\n')}\n` }];
|
|
95
|
+
},
|
|
96
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Generator } from '../generator.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generates project topology artifacts from a single source: per-service Data
|
|
4
|
+
* Connect `dataconnect.yaml`, per-connector `connector.yaml` (package/outputDir
|
|
5
|
+
* are pure string formulas), and the sync constants (`FIRESTORE_DATABASES`,
|
|
6
|
+
* `API_CODEBASES`) that would otherwise be kept in sync by hand.
|
|
7
|
+
*/
|
|
8
|
+
export declare const createConfigGenerator: () => Generator;
|
|
9
|
+
//# sourceMappingURL=config-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-generator.d.ts","sourceRoot":"","sources":["../../../../../src/lib/_internal/generators/config/config-generator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiB,SAAS,EAAoB,MAAM,iBAAiB,CAAA;AAqD5E;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,QAAO,SAuBvC,CAAA"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { constantCase, singleQuote } from '../support/naming.js';
|
|
2
|
+
import { headerFor } from '../support/header.js';
|
|
3
|
+
const DEFAULT_LOCATION = 'asia-northeast1';
|
|
4
|
+
const dataconnectYaml = (service) => {
|
|
5
|
+
const connectorDirs = service.connectors.map(c => `./connectors/${c}`);
|
|
6
|
+
return [
|
|
7
|
+
'specVersion: v1beta',
|
|
8
|
+
`serviceId: ${service.name}`,
|
|
9
|
+
`location: ${service.location ?? DEFAULT_LOCATION}`,
|
|
10
|
+
'schema:',
|
|
11
|
+
' source: ./schema',
|
|
12
|
+
...(service.database ? [' datasource:', ' postgresql:', ` database: ${service.database}`] : []),
|
|
13
|
+
`connectorDirs: [${connectorDirs.map(d => `'${d}'`).join(', ')}]`,
|
|
14
|
+
'',
|
|
15
|
+
].join('\n');
|
|
16
|
+
};
|
|
17
|
+
const connectorYaml = (service, connector) => [
|
|
18
|
+
`connectorId: ${connector}`,
|
|
19
|
+
'generate:',
|
|
20
|
+
' javascriptSdk:',
|
|
21
|
+
` outputDir: ../../../dataconnect-generated/${connector}`,
|
|
22
|
+
` package: '@dataconnect/${service.name}-generated-${connector}'`,
|
|
23
|
+
' react: true',
|
|
24
|
+
'',
|
|
25
|
+
].join('\n');
|
|
26
|
+
const constantsTs = (project, header) => {
|
|
27
|
+
const blocks = header ? [header] : [];
|
|
28
|
+
if (project.services.length > 0) {
|
|
29
|
+
const entries = project.services
|
|
30
|
+
.map(s => ` ${constantCase(s.name)}: ${singleQuote(s.database ?? s.name)},`)
|
|
31
|
+
.join('\n');
|
|
32
|
+
blocks.push(`export const FIRESTORE_DATABASES = Object.freeze({\n${entries}\n} as const)`, 'export type FirestoreDatabaseKey = keyof typeof FIRESTORE_DATABASES');
|
|
33
|
+
}
|
|
34
|
+
if (project.codebases.length > 0) {
|
|
35
|
+
const entries = project.codebases
|
|
36
|
+
.map(c => ` ${constantCase(c.service)}: ${singleQuote(c.codebase)},`)
|
|
37
|
+
.join('\n');
|
|
38
|
+
blocks.push(`export const API_CODEBASES = Object.freeze({\n${entries}\n} as const)`, 'export type ApiCodebaseKey = keyof typeof API_CODEBASES');
|
|
39
|
+
}
|
|
40
|
+
return `${blocks.join('\n\n')}\n`;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Generates project topology artifacts from a single source: per-service Data
|
|
44
|
+
* Connect `dataconnect.yaml`, per-connector `connector.yaml` (package/outputDir
|
|
45
|
+
* are pure string formulas), and the sync constants (`FIRESTORE_DATABASES`,
|
|
46
|
+
* `API_CODEBASES`) that would otherwise be kept in sync by hand.
|
|
47
|
+
*/
|
|
48
|
+
export const createConfigGenerator = () => ({
|
|
49
|
+
name: 'config',
|
|
50
|
+
description: 'Data Connect service/connector configs + project sync constants',
|
|
51
|
+
generate(ir, context) {
|
|
52
|
+
const project = ir.project;
|
|
53
|
+
if (!project) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
const files = [];
|
|
57
|
+
for (const service of project.services) {
|
|
58
|
+
files.push({ path: `${service.name}/dataconnect.yaml`, content: dataconnectYaml(service) });
|
|
59
|
+
for (const connector of service.connectors) {
|
|
60
|
+
files.push({
|
|
61
|
+
path: `${service.name}/connectors/${connector}/connector.yaml`,
|
|
62
|
+
content: connectorYaml(service, connector),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (project.services.length > 0 || project.codebases.length > 0) {
|
|
67
|
+
files.push({ path: 'constants.ts', content: constantsTs(project, headerFor(context)) });
|
|
68
|
+
}
|
|
69
|
+
return files;
|
|
70
|
+
},
|
|
71
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Generator } from '../generator.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generates typed adapters that convert between a Data Connect row (json and
|
|
4
|
+
* embedded-model fields typed as `Any`) and the logical TypeScript type,
|
|
5
|
+
* restoring the logical type through the runtime `fromAny`/`toAny` helpers.
|
|
6
|
+
*/
|
|
7
|
+
export declare const createDataConnectAdapterGenerator: () => Generator;
|
|
8
|
+
//# sourceMappingURL=adapter-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter-generator.d.ts","sourceRoot":"","sources":["../../../../../src/lib/_internal/generators/data-connect/adapter-generator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiB,SAAS,EAAoB,MAAM,iBAAiB,CAAA;AAsI5E;;;;GAIG;AACH,eAAO,MAAM,iCAAiC,QAAO,SAmBnD,CAAA"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { camelCase } from '../support/naming.js';
|
|
2
|
+
import { headerBlocks } from '../support/header.js';
|
|
3
|
+
import { isRelation, relationFkName, relationFkType } from '../support/relations.js';
|
|
4
|
+
const SCALAR_TS = {
|
|
5
|
+
string: 'string',
|
|
6
|
+
int: 'number',
|
|
7
|
+
int64: 'number',
|
|
8
|
+
float: 'number',
|
|
9
|
+
boolean: 'boolean',
|
|
10
|
+
timestamp: 'string',
|
|
11
|
+
date: 'string',
|
|
12
|
+
json: 'Json',
|
|
13
|
+
id: 'string',
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Whether the field collapses to `Any` in Data Connect (json or *embedded*
|
|
17
|
+
* model). Relations are FK id scalars, not `Any`, so they are excluded.
|
|
18
|
+
*/
|
|
19
|
+
const isAnyBacked = (field) => (field.type.kind === 'model' && !isRelation(field)) ||
|
|
20
|
+
field.type.kind === 'unresolved' ||
|
|
21
|
+
(field.type.kind === 'scalar' && field.type.name === 'json');
|
|
22
|
+
/** The property name of the field on both the row and the logical type. */
|
|
23
|
+
const fieldKey = (field) => (isRelation(field) ? relationFkName(field) : field.name);
|
|
24
|
+
const logicalBase = (ref) => {
|
|
25
|
+
switch (ref.kind) {
|
|
26
|
+
case 'scalar':
|
|
27
|
+
return SCALAR_TS[ref.name];
|
|
28
|
+
case 'enum':
|
|
29
|
+
case 'model':
|
|
30
|
+
return ref.name;
|
|
31
|
+
default:
|
|
32
|
+
return 'unknown';
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const withList = (base, list) => (list ? `${base}[]` : base);
|
|
36
|
+
const rowFieldType = (field, ir) => {
|
|
37
|
+
if (field.literal !== undefined) {
|
|
38
|
+
return withList(`'${field.literal}'`, field.list);
|
|
39
|
+
}
|
|
40
|
+
if (isRelation(field)) {
|
|
41
|
+
return withList(SCALAR_TS[relationFkType(ir, field)], field.list);
|
|
42
|
+
}
|
|
43
|
+
const base = isAnyBacked(field) ? 'Any' : logicalBase(field.type);
|
|
44
|
+
return withList(base, field.list);
|
|
45
|
+
};
|
|
46
|
+
const optionalMark = (field) => (field.optional ? '?' : '');
|
|
47
|
+
/** Expression that maps a Data Connect row field back to the logical value. */
|
|
48
|
+
const fromExpr = (field) => {
|
|
49
|
+
const access = `row.${fieldKey(field)}`;
|
|
50
|
+
if (!isAnyBacked(field)) {
|
|
51
|
+
return access;
|
|
52
|
+
}
|
|
53
|
+
const base = logicalBase(field.type);
|
|
54
|
+
const one = (value) => `fromAny<${base}>(${value})`;
|
|
55
|
+
if (field.list) {
|
|
56
|
+
const mapped = `${access}${field.optional ? '?' : ''}.map(item => ${one('item')})`;
|
|
57
|
+
return mapped;
|
|
58
|
+
}
|
|
59
|
+
if (field.optional) {
|
|
60
|
+
return `${access} === undefined ? undefined : ${one(access)}`;
|
|
61
|
+
}
|
|
62
|
+
return one(access);
|
|
63
|
+
};
|
|
64
|
+
/** Expression that maps a logical value to the Data Connect row field. */
|
|
65
|
+
const toExpr = (field) => {
|
|
66
|
+
const access = `value.${fieldKey(field)}`;
|
|
67
|
+
if (!isAnyBacked(field)) {
|
|
68
|
+
return access;
|
|
69
|
+
}
|
|
70
|
+
if (field.list) {
|
|
71
|
+
return `${access}${field.optional ? '?' : ''}.map(item => toAny(item))`;
|
|
72
|
+
}
|
|
73
|
+
if (field.optional) {
|
|
74
|
+
return `${access} === undefined ? undefined : toAny(${access})`;
|
|
75
|
+
}
|
|
76
|
+
return `toAny(${access})`;
|
|
77
|
+
};
|
|
78
|
+
const renderRowInterface = (model, ir) => {
|
|
79
|
+
const fields = model.fields
|
|
80
|
+
.map(field => ` ${fieldKey(field)}${optionalMark(field)}: ${rowFieldType(field, ir)}`)
|
|
81
|
+
.join('\n');
|
|
82
|
+
return `export interface ${model.name}Row {\n${fields}\n}`;
|
|
83
|
+
};
|
|
84
|
+
const renderAdapter = (model) => {
|
|
85
|
+
const adapterName = `${camelCase(model.name)}Adapter`;
|
|
86
|
+
const fromFields = model.fields.map(field => ` ${fieldKey(field)}: ${fromExpr(field)},`).join('\n');
|
|
87
|
+
const toFields = model.fields.map(field => ` ${fieldKey(field)}: ${toExpr(field)},`).join('\n');
|
|
88
|
+
return [
|
|
89
|
+
`export const ${adapterName} = {`,
|
|
90
|
+
` fromDataConnect(row: ${model.name}Row): ${model.name} {`,
|
|
91
|
+
` return {`,
|
|
92
|
+
fromFields,
|
|
93
|
+
` }`,
|
|
94
|
+
` },`,
|
|
95
|
+
` toDataConnect(value: ${model.name}): ${model.name}Row {`,
|
|
96
|
+
` return {`,
|
|
97
|
+
toFields,
|
|
98
|
+
` }`,
|
|
99
|
+
` },`,
|
|
100
|
+
`}`,
|
|
101
|
+
].join('\n');
|
|
102
|
+
};
|
|
103
|
+
/** Collect the logical type names the adapter references, to import from types.ts. */
|
|
104
|
+
const collectTypeImports = (ir) => {
|
|
105
|
+
const names = new Set();
|
|
106
|
+
for (const model of ir.models) {
|
|
107
|
+
names.add(model.name);
|
|
108
|
+
for (const field of model.fields) {
|
|
109
|
+
const ref = field.type;
|
|
110
|
+
// Relations become scalar FK ids, so the related model type is not imported.
|
|
111
|
+
if (ref.kind === 'enum' || (ref.kind === 'model' && !isRelation(field))) {
|
|
112
|
+
names.add(ref.name);
|
|
113
|
+
}
|
|
114
|
+
if (ref.kind === 'scalar' && ref.name === 'json') {
|
|
115
|
+
names.add('Json');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return [...names].sort();
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* Generates typed adapters that convert between a Data Connect row (json and
|
|
123
|
+
* embedded-model fields typed as `Any`) and the logical TypeScript type,
|
|
124
|
+
* restoring the logical type through the runtime `fromAny`/`toAny` helpers.
|
|
125
|
+
*/
|
|
126
|
+
export const createDataConnectAdapterGenerator = () => ({
|
|
127
|
+
name: 'data-connect-adapter',
|
|
128
|
+
description: 'Adapters between Data Connect Any rows and logical types',
|
|
129
|
+
generate(ir, context) {
|
|
130
|
+
const typeImports = collectTypeImports(ir);
|
|
131
|
+
const blocks = [...headerBlocks(context)];
|
|
132
|
+
if (typeImports.length > 0) {
|
|
133
|
+
blocks.push(`import type { ${typeImports.join(', ')} } from './types.js'`);
|
|
134
|
+
}
|
|
135
|
+
blocks.push("import { fromAny, toAny, type Any } from 'firebase-contract/runtime'");
|
|
136
|
+
for (const model of ir.models) {
|
|
137
|
+
blocks.push(renderRowInterface(model, ir));
|
|
138
|
+
blocks.push(renderAdapter(model));
|
|
139
|
+
}
|
|
140
|
+
return [{ path: 'data-connect-adapters.ts', content: `${blocks.join('\n\n')}\n` }];
|
|
141
|
+
},
|
|
142
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Generator } from '../generator.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generates a Data Connect GraphQL schema (`type ... @table`). JSON and embedded
|
|
4
|
+
* model fields become the `Any` scalar while retaining their logical type as a
|
|
5
|
+
* comment, matching the "JSON is Any, logical type preserved" rule.
|
|
6
|
+
*/
|
|
7
|
+
export declare const createDataConnectGraphqlGenerator: () => Generator;
|
|
8
|
+
/**
|
|
9
|
+
* Split variant matching the real Data Connect layout: one file per table at
|
|
10
|
+
* `schema/<table_name>.gql`, with each enum co-located in the file of the first
|
|
11
|
+
* model that references it (leftover enums go to `schema/_enums.gql`). The `Any`
|
|
12
|
+
* scalar is not declared — Data Connect provides it built-in.
|
|
13
|
+
*/
|
|
14
|
+
export declare const createDataConnectGraphqlSplitGenerator: () => Generator;
|
|
15
|
+
//# sourceMappingURL=graphql-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphql-generator.d.ts","sourceRoot":"","sources":["../../../../../src/lib/_internal/generators/data-connect/graphql-generator.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,SAAS,EAAoB,MAAM,iBAAiB,CAAA;AA8I5E;;;;GAIG;AACH,eAAO,MAAM,iCAAiC,QAAO,SA+BnD,CAAA;AAaF;;;;;GAKG;AACH,eAAO,MAAM,sCAAsC,QAAO,SAmCxD,CAAA"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { headerBlocks } from '../support/header.js';
|
|
2
|
+
import { constantCase, pluralize, snakeCase } from '../support/naming.js';
|
|
3
|
+
import { isRelation } from '../support/relations.js';
|
|
4
|
+
// Data Connect built-in scalars. `json` and embedded model objects are stored
|
|
5
|
+
// as `Any` — the logical type is preserved in a comment and restored by the
|
|
6
|
+
// Data Connect adapter generator.
|
|
7
|
+
const SCALAR_GQL = {
|
|
8
|
+
string: 'String',
|
|
9
|
+
int: 'Int',
|
|
10
|
+
int64: 'Int64',
|
|
11
|
+
float: 'Float',
|
|
12
|
+
boolean: 'Boolean',
|
|
13
|
+
timestamp: 'Timestamp',
|
|
14
|
+
date: 'Date',
|
|
15
|
+
json: 'Any',
|
|
16
|
+
id: 'UUID',
|
|
17
|
+
};
|
|
18
|
+
const gqlOf = (rename, name) => rename.get(name) ?? name;
|
|
19
|
+
const renderRef = (ref, rename) => {
|
|
20
|
+
switch (ref.kind) {
|
|
21
|
+
case 'scalar':
|
|
22
|
+
return { gql: SCALAR_GQL[ref.name] };
|
|
23
|
+
case 'enum':
|
|
24
|
+
return { gql: gqlOf(rename, ref.name) };
|
|
25
|
+
case 'model':
|
|
26
|
+
return { gql: 'Any', logical: ref.name };
|
|
27
|
+
default:
|
|
28
|
+
return { gql: 'Any', logical: ref.name };
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const defaultExpr = (value) => {
|
|
32
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
33
|
+
return `"${value}"`;
|
|
34
|
+
return `"'${String(value)}'"`;
|
|
35
|
+
};
|
|
36
|
+
/** Derive the Data Connect column dataType for a field, if any. */
|
|
37
|
+
const colDataType = (field) => {
|
|
38
|
+
if (field.col)
|
|
39
|
+
return field.col;
|
|
40
|
+
// Int64 surrogate primary keys map to bigserial.
|
|
41
|
+
if (field.isId && field.type.kind === 'scalar' && (field.type.name === 'int' || field.type.name === 'int64'))
|
|
42
|
+
return 'bigserial';
|
|
43
|
+
return undefined;
|
|
44
|
+
};
|
|
45
|
+
const fieldDirectives = (field) => {
|
|
46
|
+
const parts = [];
|
|
47
|
+
const col = colDataType(field);
|
|
48
|
+
if (col)
|
|
49
|
+
parts.push(`@col(dataType: "${col}")`);
|
|
50
|
+
if (field.unique)
|
|
51
|
+
parts.push('@unique');
|
|
52
|
+
if (field.default !== undefined)
|
|
53
|
+
parts.push(`@default(expr: ${defaultExpr(field.default)})`);
|
|
54
|
+
return parts.length > 0 ? ` ${parts.join(' ')}` : '';
|
|
55
|
+
};
|
|
56
|
+
/** Render a (possibly multi-line) description as comment lines at `indent`. */
|
|
57
|
+
const commentLines = (description, indent) => description ? description.split('\n').map(line => `${indent}# ${line}`.trimEnd()) : [];
|
|
58
|
+
const renderField = (field, rename) => {
|
|
59
|
+
const nullable = field.optional ? '' : '!';
|
|
60
|
+
const directives = fieldDirectives(field);
|
|
61
|
+
// Descriptions render as full comment lines above the field (matching real
|
|
62
|
+
// Data Connect schema files), so multi-line descriptions survive verbatim.
|
|
63
|
+
const above = commentLines(field.description, ' ');
|
|
64
|
+
// Relation: emit a Data Connect relation to the related table (DC auto-creates
|
|
65
|
+
// the FK column). This is distinct from an embedded model, which becomes `Any`.
|
|
66
|
+
if (isRelation(field) && field.type.kind === 'model') {
|
|
67
|
+
const gqlName = gqlOf(rename, field.type.name);
|
|
68
|
+
const inner = field.list ? `[${gqlName}!]` : gqlName;
|
|
69
|
+
return [...above, ` ${field.name}: ${inner}${nullable}${directives}`].join('\n');
|
|
70
|
+
}
|
|
71
|
+
const { gql, logical } = renderRef(field.type, rename);
|
|
72
|
+
const inner = field.list ? `[${gql}!]` : gql;
|
|
73
|
+
const trailing = logical ? ` # logical: ${logical}${field.list ? '[]' : ''}` : '';
|
|
74
|
+
return [...above, ` ${field.name}: ${inner}${nullable}${directives}${trailing}`].join('\n');
|
|
75
|
+
};
|
|
76
|
+
const tableName = (model) => model.table ?? pluralize(snakeCase(model.name));
|
|
77
|
+
/**
|
|
78
|
+
* Whether a model is a real table (has a primary key) as opposed to an embedded
|
|
79
|
+
* value object (jsonb shape, union variant, …) which must NOT get a `@table`.
|
|
80
|
+
*/
|
|
81
|
+
const buildRename = (ir) => {
|
|
82
|
+
const rename = new Map();
|
|
83
|
+
for (const m of ir.models)
|
|
84
|
+
if (m.gqlName)
|
|
85
|
+
rename.set(m.name, m.gqlName);
|
|
86
|
+
for (const e of ir.enums)
|
|
87
|
+
if (e.gqlName)
|
|
88
|
+
rename.set(e.name, e.gqlName);
|
|
89
|
+
return rename;
|
|
90
|
+
};
|
|
91
|
+
const isTable = (model) => model.key.length > 0 || model.fields.some(f => f.isId);
|
|
92
|
+
/** The `@table(...)` directive plus any type-level `@unique`/`@index` directives. */
|
|
93
|
+
const tableDirectiveList = (model) => {
|
|
94
|
+
const keyFields = model.key.length > 0 ? model.key : model.fields.filter(f => f.isId).map(f => f.name);
|
|
95
|
+
const name = tableName(model);
|
|
96
|
+
const args = [`name: "${name}"`];
|
|
97
|
+
if (keyFields.length > 0) {
|
|
98
|
+
args.push(`key: [${keyFields.map(f => `"${f}"`).join(', ')}]`);
|
|
99
|
+
}
|
|
100
|
+
const directives = [`@table(${args.join(', ')})`];
|
|
101
|
+
for (const index of model.indexes) {
|
|
102
|
+
const fieldsArg = `fields: [${index.fields.map(f => `"${f}"`).join(', ')}]`;
|
|
103
|
+
const kind = index.unique ? '@unique' : '@index';
|
|
104
|
+
const nameArg = index.unique ? 'indexName' : 'name';
|
|
105
|
+
const idxName = index.name ?? `${name}_${index.fields.join('_')}_${index.unique ? 'uidx' : 'idx'}`;
|
|
106
|
+
if (index.expand) {
|
|
107
|
+
// Expanded argument form (one per line), matching hand-written files.
|
|
108
|
+
directives.push(`${kind}(\n ${fieldsArg}\n ${nameArg}: "${idxName}"\n )`);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
directives.push(`${kind}(${fieldsArg}, ${nameArg}: "${idxName}")`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return directives;
|
|
115
|
+
};
|
|
116
|
+
/** `type X @table(...) {` — or the multi-line per-directive form when requested. */
|
|
117
|
+
const typeHeader = (model, gqlName) => {
|
|
118
|
+
const directives = tableDirectiveList(model);
|
|
119
|
+
if (model.directives === 'multi') {
|
|
120
|
+
return `type ${gqlName}\n ${directives.join('\n ')} {`;
|
|
121
|
+
}
|
|
122
|
+
return `type ${gqlName} ${directives.join(' ')} {`;
|
|
123
|
+
};
|
|
124
|
+
const commentBanner = (header) => header
|
|
125
|
+
.split('\n')
|
|
126
|
+
.map(line => line.replace(/^\/\/ ?/, '# '))
|
|
127
|
+
.join('\n');
|
|
128
|
+
/**
|
|
129
|
+
* Generates a Data Connect GraphQL schema (`type ... @table`). JSON and embedded
|
|
130
|
+
* model fields become the `Any` scalar while retaining their logical type as a
|
|
131
|
+
* comment, matching the "JSON is Any, logical type preserved" rule.
|
|
132
|
+
*/
|
|
133
|
+
export const createDataConnectGraphqlGenerator = () => ({
|
|
134
|
+
name: 'data-connect-graphql',
|
|
135
|
+
description: 'Data Connect GraphQL schema',
|
|
136
|
+
generate(ir, context) {
|
|
137
|
+
const blocks = [...headerBlocks(context, commentBanner)];
|
|
138
|
+
const usesAny = ir.models.some(model => model.fields.some(field => (field.type.kind === 'model' && !isRelation(field)) ||
|
|
139
|
+
(field.type.kind === 'scalar' && field.type.name === 'json')));
|
|
140
|
+
if (usesAny) {
|
|
141
|
+
blocks.push('scalar Any');
|
|
142
|
+
}
|
|
143
|
+
const rename = buildRename(ir);
|
|
144
|
+
for (const irEnum of ir.enums) {
|
|
145
|
+
blocks.push(renderEnumGql(irEnum, rename));
|
|
146
|
+
}
|
|
147
|
+
for (const model of ir.models.filter(isTable)) {
|
|
148
|
+
const fields = model.fields.map(field => renderField(field, rename)).join('\n');
|
|
149
|
+
const doc = commentLines(model.description, '');
|
|
150
|
+
blocks.push([...doc, `${typeHeader(model, gqlOf(rename, model.name))}\n${fields}\n}`].join('\n'));
|
|
151
|
+
if (model.footer)
|
|
152
|
+
blocks.push(commentLines(model.footer, '').join('\n'));
|
|
153
|
+
}
|
|
154
|
+
return [{ path: 'schema.gql', content: `${blocks.join('\n\n')}\n` }];
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
const renderEnumGql = (irEnum, rename) => {
|
|
158
|
+
const lines = [...commentLines(irEnum.description, '')];
|
|
159
|
+
lines.push(`enum ${gqlOf(rename, irEnum.name)} {`);
|
|
160
|
+
for (const value of irEnum.values) {
|
|
161
|
+
lines.push(...commentLines(irEnum.valueComments?.[value], ' '));
|
|
162
|
+
lines.push(` ${constantCase(value)}`);
|
|
163
|
+
}
|
|
164
|
+
lines.push('}');
|
|
165
|
+
return lines.join('\n');
|
|
166
|
+
};
|
|
167
|
+
/**
|
|
168
|
+
* Split variant matching the real Data Connect layout: one file per table at
|
|
169
|
+
* `schema/<table_name>.gql`, with each enum co-located in the file of the first
|
|
170
|
+
* model that references it (leftover enums go to `schema/_enums.gql`). The `Any`
|
|
171
|
+
* scalar is not declared — Data Connect provides it built-in.
|
|
172
|
+
*/
|
|
173
|
+
export const createDataConnectGraphqlSplitGenerator = () => ({
|
|
174
|
+
name: 'data-connect-graphql-split',
|
|
175
|
+
description: 'Data Connect GraphQL schema, one file per table (schema/<table>.gql)',
|
|
176
|
+
generate(ir, context) {
|
|
177
|
+
const banner = headerBlocks(context, commentBanner);
|
|
178
|
+
const rename = buildRename(ir);
|
|
179
|
+
const tables = ir.models.filter(isTable);
|
|
180
|
+
// Assign each enum to the first *table* that references it (embedded value
|
|
181
|
+
// objects do not get schema files, so they cannot host enums).
|
|
182
|
+
const enumHome = new Map();
|
|
183
|
+
for (const model of tables) {
|
|
184
|
+
for (const field of model.fields) {
|
|
185
|
+
if (field.type.kind === 'enum' && !enumHome.has(field.type.name)) {
|
|
186
|
+
enumHome.set(field.type.name, model.name);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const files = [];
|
|
191
|
+
for (const model of tables) {
|
|
192
|
+
const blocks = [...banner];
|
|
193
|
+
for (const irEnum of ir.enums) {
|
|
194
|
+
if (enumHome.get(irEnum.name) === model.name) {
|
|
195
|
+
blocks.push(renderEnumGql(irEnum, rename));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const fields = model.fields.map(field => renderField(field, rename)).join('\n');
|
|
199
|
+
const doc = commentLines(model.description, '');
|
|
200
|
+
blocks.push([...doc, `${typeHeader(model, gqlOf(rename, model.name))}\n${fields}\n}`].join('\n'));
|
|
201
|
+
if (model.footer)
|
|
202
|
+
blocks.push(commentLines(model.footer, '').join('\n'));
|
|
203
|
+
files.push({ path: `schema/${tableName(model)}.gql`, content: `${blocks.join('\n\n')}\n` });
|
|
204
|
+
}
|
|
205
|
+
// Enums referenced only by embedded value objects are TS/Zod-level types and
|
|
206
|
+
// do not exist in the Data Connect schema — they are intentionally skipped.
|
|
207
|
+
return files;
|
|
208
|
+
},
|
|
209
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Generator } from '../generator.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generates Data Connect operations: `.gql` files with `mutation`/`query`
|
|
4
|
+
* definitions (over the auto-generated `<table>_insert/update/upsert/delete`
|
|
5
|
+
* resolvers, with `@auth` directives) plus matching TS `Variables`/`Result`
|
|
6
|
+
* types. Operations are routed per **connector** — an operation may target
|
|
7
|
+
* several connectors (`connectors: [app, api]`) and is emitted into each, under
|
|
8
|
+
* `<connector>/operations.gql`. Operations without a connector go to the root.
|
|
9
|
+
*/
|
|
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
|
+
//# sourceMappingURL=operations-generator.d.ts.map
|
|
@@ -0,0 +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"}
|