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.
Files changed (152) hide show
  1. package/README.md +560 -0
  2. package/dist/index.d.ts +15 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +12 -0
  5. package/dist/lib/_internal/compiler/compiler.d.ts +71 -0
  6. package/dist/lib/_internal/compiler/compiler.d.ts.map +1 -0
  7. package/dist/lib/_internal/compiler/compiler.js +104 -0
  8. package/dist/lib/_internal/compiler/index.d.ts +3 -0
  9. package/dist/lib/_internal/compiler/index.d.ts.map +1 -0
  10. package/dist/lib/_internal/compiler/index.js +1 -0
  11. package/dist/lib/_internal/diagnostics.d.ts +29 -0
  12. package/dist/lib/_internal/diagnostics.d.ts.map +1 -0
  13. package/dist/lib/_internal/diagnostics.js +26 -0
  14. package/dist/lib/_internal/format-diagnostics.d.ts +8 -0
  15. package/dist/lib/_internal/format-diagnostics.d.ts.map +1 -0
  16. package/dist/lib/_internal/format-diagnostics.js +10 -0
  17. package/dist/lib/_internal/generators/api/api-dto-generator.d.ts +8 -0
  18. package/dist/lib/_internal/generators/api/api-dto-generator.d.ts.map +1 -0
  19. package/dist/lib/_internal/generators/api/api-dto-generator.js +134 -0
  20. package/dist/lib/_internal/generators/api/api-types-generator.d.ts +8 -0
  21. package/dist/lib/_internal/generators/api/api-types-generator.d.ts.map +1 -0
  22. package/dist/lib/_internal/generators/api/api-types-generator.js +105 -0
  23. package/dist/lib/_internal/generators/api/api-validation-generator.d.ts +7 -0
  24. package/dist/lib/_internal/generators/api/api-validation-generator.d.ts.map +1 -0
  25. package/dist/lib/_internal/generators/api/api-validation-generator.js +73 -0
  26. package/dist/lib/_internal/generators/api/task-payload-generator.d.ts +9 -0
  27. package/dist/lib/_internal/generators/api/task-payload-generator.d.ts.map +1 -0
  28. package/dist/lib/_internal/generators/api/task-payload-generator.js +96 -0
  29. package/dist/lib/_internal/generators/config/config-generator.d.ts +9 -0
  30. package/dist/lib/_internal/generators/config/config-generator.d.ts.map +1 -0
  31. package/dist/lib/_internal/generators/config/config-generator.js +71 -0
  32. package/dist/lib/_internal/generators/data-connect/adapter-generator.d.ts +8 -0
  33. package/dist/lib/_internal/generators/data-connect/adapter-generator.d.ts.map +1 -0
  34. package/dist/lib/_internal/generators/data-connect/adapter-generator.js +142 -0
  35. package/dist/lib/_internal/generators/data-connect/graphql-generator.d.ts +15 -0
  36. package/dist/lib/_internal/generators/data-connect/graphql-generator.d.ts.map +1 -0
  37. package/dist/lib/_internal/generators/data-connect/graphql-generator.js +209 -0
  38. package/dist/lib/_internal/generators/data-connect/operations-generator.d.ts +18 -0
  39. package/dist/lib/_internal/generators/data-connect/operations-generator.d.ts.map +1 -0
  40. package/dist/lib/_internal/generators/data-connect/operations-generator.js +539 -0
  41. package/dist/lib/_internal/generators/firestore/firestore-projection-generator.d.ts +11 -0
  42. package/dist/lib/_internal/generators/firestore/firestore-projection-generator.d.ts.map +1 -0
  43. package/dist/lib/_internal/generators/firestore/firestore-projection-generator.js +112 -0
  44. package/dist/lib/_internal/generators/firestore/firestore-split-generator.d.ts +11 -0
  45. package/dist/lib/_internal/generators/firestore/firestore-split-generator.d.ts.map +1 -0
  46. package/dist/lib/_internal/generators/firestore/firestore-split-generator.js +375 -0
  47. package/dist/lib/_internal/generators/firestore/firestore-type-generator.d.ts +12 -0
  48. package/dist/lib/_internal/generators/firestore/firestore-type-generator.d.ts.map +1 -0
  49. package/dist/lib/_internal/generators/firestore/firestore-type-generator.js +81 -0
  50. package/dist/lib/_internal/generators/generator.d.ts +23 -0
  51. package/dist/lib/_internal/generators/generator.d.ts.map +1 -0
  52. package/dist/lib/_internal/generators/generator.js +1 -0
  53. package/dist/lib/_internal/generators/id/id-codec-generator.d.ts +16 -0
  54. package/dist/lib/_internal/generators/id/id-codec-generator.d.ts.map +1 -0
  55. package/dist/lib/_internal/generators/id/id-codec-generator.js +140 -0
  56. package/dist/lib/_internal/generators/index.d.ts +31 -0
  57. package/dist/lib/_internal/generators/index.d.ts.map +1 -0
  58. package/dist/lib/_internal/generators/index.js +64 -0
  59. package/dist/lib/_internal/generators/registry.d.ts +15 -0
  60. package/dist/lib/_internal/generators/registry.d.ts.map +1 -0
  61. package/dist/lib/_internal/generators/registry.js +24 -0
  62. package/dist/lib/_internal/generators/sql/sql-migration-generator.d.ts +9 -0
  63. package/dist/lib/_internal/generators/sql/sql-migration-generator.d.ts.map +1 -0
  64. package/dist/lib/_internal/generators/sql/sql-migration-generator.js +44 -0
  65. package/dist/lib/_internal/generators/support/constraints.d.ts +8 -0
  66. package/dist/lib/_internal/generators/support/constraints.d.ts.map +1 -0
  67. package/dist/lib/_internal/generators/support/constraints.js +39 -0
  68. package/dist/lib/_internal/generators/support/header.d.ts +13 -0
  69. package/dist/lib/_internal/generators/support/header.d.ts.map +1 -0
  70. package/dist/lib/_internal/generators/support/header.js +17 -0
  71. package/dist/lib/_internal/generators/support/naming.d.ts +10 -0
  72. package/dist/lib/_internal/generators/support/naming.d.ts.map +1 -0
  73. package/dist/lib/_internal/generators/support/naming.js +30 -0
  74. package/dist/lib/_internal/generators/support/relations.d.ts +12 -0
  75. package/dist/lib/_internal/generators/support/relations.d.ts.map +1 -0
  76. package/dist/lib/_internal/generators/support/relations.js +11 -0
  77. package/dist/lib/_internal/generators/support/split.d.ts +34 -0
  78. package/dist/lib/_internal/generators/support/split.d.ts.map +1 -0
  79. package/dist/lib/_internal/generators/support/split.js +75 -0
  80. package/dist/lib/_internal/generators/typescript/typescript-generator.d.ts +18 -0
  81. package/dist/lib/_internal/generators/typescript/typescript-generator.d.ts.map +1 -0
  82. package/dist/lib/_internal/generators/typescript/typescript-generator.js +99 -0
  83. package/dist/lib/_internal/generators/typescript/typescript-split-generator.d.ts +10 -0
  84. package/dist/lib/_internal/generators/typescript/typescript-split-generator.d.ts.map +1 -0
  85. package/dist/lib/_internal/generators/typescript/typescript-split-generator.js +175 -0
  86. package/dist/lib/_internal/generators/union/union-generator.d.ts +8 -0
  87. package/dist/lib/_internal/generators/union/union-generator.d.ts.map +1 -0
  88. package/dist/lib/_internal/generators/union/union-generator.js +37 -0
  89. package/dist/lib/_internal/generators/zod/zod-generator.d.ts +7 -0
  90. package/dist/lib/_internal/generators/zod/zod-generator.d.ts.map +1 -0
  91. package/dist/lib/_internal/generators/zod/zod-generator.js +68 -0
  92. package/dist/lib/_internal/generators/zod/zod-split-generator.d.ts +9 -0
  93. package/dist/lib/_internal/generators/zod/zod-split-generator.d.ts.map +1 -0
  94. package/dist/lib/_internal/generators/zod/zod-split-generator.js +136 -0
  95. package/dist/lib/_internal/ir/build-ir.d.ts +18 -0
  96. package/dist/lib/_internal/ir/build-ir.d.ts.map +1 -0
  97. package/dist/lib/_internal/ir/build-ir.js +430 -0
  98. package/dist/lib/_internal/ir/ir.d.ts +393 -0
  99. package/dist/lib/_internal/ir/ir.d.ts.map +1 -0
  100. package/dist/lib/_internal/ir/ir.js +22 -0
  101. package/dist/lib/_internal/parser/parse.d.ts +9 -0
  102. package/dist/lib/_internal/parser/parse.d.ts.map +1 -0
  103. package/dist/lib/_internal/parser/parse.js +682 -0
  104. package/dist/lib/_internal/parser/raw-document.d.ts +230 -0
  105. package/dist/lib/_internal/parser/raw-document.d.ts.map +1 -0
  106. package/dist/lib/_internal/parser/raw-document.js +6 -0
  107. package/dist/lib/_internal/resolver/import-resolver.d.ts +25 -0
  108. package/dist/lib/_internal/resolver/import-resolver.d.ts.map +1 -0
  109. package/dist/lib/_internal/resolver/import-resolver.js +73 -0
  110. package/dist/lib/_internal/resolver/module-loader.d.ts +25 -0
  111. package/dist/lib/_internal/resolver/module-loader.d.ts.map +1 -0
  112. package/dist/lib/_internal/resolver/module-loader.js +62 -0
  113. package/dist/lib/_internal/runtime/data-connect-adapter.d.ts +25 -0
  114. package/dist/lib/_internal/runtime/data-connect-adapter.d.ts.map +1 -0
  115. package/dist/lib/_internal/runtime/data-connect-adapter.js +24 -0
  116. package/dist/lib/_internal/runtime/index.d.ts +3 -0
  117. package/dist/lib/_internal/runtime/index.d.ts.map +1 -0
  118. package/dist/lib/_internal/runtime/index.js +1 -0
  119. package/dist/lib/_internal/validation/rules.d.ts +32 -0
  120. package/dist/lib/_internal/validation/rules.d.ts.map +1 -0
  121. package/dist/lib/_internal/validation/rules.js +282 -0
  122. package/dist/lib/_internal/validation/validate.d.ts +9 -0
  123. package/dist/lib/_internal/validation/validate.d.ts.map +1 -0
  124. package/dist/lib/_internal/validation/validate.js +6 -0
  125. package/dist/lib/cli/commands/generate.d.ts +3 -0
  126. package/dist/lib/cli/commands/generate.d.ts.map +1 -0
  127. package/dist/lib/cli/commands/generate.js +66 -0
  128. package/dist/lib/cli/commands/index.d.ts +4 -0
  129. package/dist/lib/cli/commands/index.d.ts.map +1 -0
  130. package/dist/lib/cli/commands/index.js +11 -0
  131. package/dist/lib/cli/commands/init.d.ts +3 -0
  132. package/dist/lib/cli/commands/init.d.ts.map +1 -0
  133. package/dist/lib/cli/commands/init.js +72 -0
  134. package/dist/lib/cli/commands/inspect.d.ts +3 -0
  135. package/dist/lib/cli/commands/inspect.d.ts.map +1 -0
  136. package/dist/lib/cli/commands/inspect.js +23 -0
  137. package/dist/lib/cli/commands/validate.d.ts +3 -0
  138. package/dist/lib/cli/commands/validate.d.ts.map +1 -0
  139. package/dist/lib/cli/commands/validate.js +23 -0
  140. package/dist/lib/cli/config.d.ts +12 -0
  141. package/dist/lib/cli/config.d.ts.map +1 -0
  142. package/dist/lib/cli/config.js +20 -0
  143. package/dist/lib/cli/index.d.ts +3 -0
  144. package/dist/lib/cli/index.d.ts.map +1 -0
  145. package/dist/lib/cli/index.js +13 -0
  146. package/dist/lib/cli/version.d.ts +3 -0
  147. package/dist/lib/cli/version.d.ts.map +1 -0
  148. package/dist/lib/cli/version.js +15 -0
  149. package/dist/lib/modules/runtime.d.ts +2 -0
  150. package/dist/lib/modules/runtime.d.ts.map +1 -0
  151. package/dist/lib/modules/runtime.js +3 -0
  152. package/package.json +60 -0
@@ -0,0 +1,430 @@
1
+ import { error } from '../diagnostics.js';
2
+ import { isScalarType, } from './ir.js';
3
+ const resolveTypeRef = (typeName, enumNames, modelNames) => {
4
+ if (isScalarType(typeName)) {
5
+ return { kind: 'scalar', name: typeName };
6
+ }
7
+ if (enumNames.has(typeName)) {
8
+ return { kind: 'enum', name: typeName };
9
+ }
10
+ if (modelNames.has(typeName)) {
11
+ return { kind: 'model', name: typeName };
12
+ }
13
+ return { kind: 'unresolved', name: typeName };
14
+ };
15
+ const buildField = (name, raw, enumNames, modelNames) => {
16
+ const constraints = {};
17
+ if (raw.min !== undefined)
18
+ constraints.min = raw.min;
19
+ if (raw.max !== undefined)
20
+ constraints.max = raw.max;
21
+ if (raw.minLength !== undefined)
22
+ constraints.minLength = raw.minLength;
23
+ if (raw.maxLength !== undefined)
24
+ constraints.maxLength = raw.maxLength;
25
+ if (raw.pattern !== undefined)
26
+ constraints.pattern = raw.pattern;
27
+ if (raw.nonempty !== undefined)
28
+ constraints.nonempty = raw.nonempty;
29
+ if (raw.email !== undefined)
30
+ constraints.email = raw.email;
31
+ if (raw.url !== undefined)
32
+ constraints.url = raw.url;
33
+ const field = {
34
+ name,
35
+ type: resolveTypeRef(raw.type, enumNames, modelNames),
36
+ constraints,
37
+ optional: raw.optional ?? false,
38
+ list: raw.list ?? false,
39
+ isId: raw.id ?? false,
40
+ unique: raw.unique ?? false,
41
+ relation: raw.relation ?? false,
42
+ };
43
+ if (raw.description !== undefined)
44
+ field.description = raw.description;
45
+ if (raw.nullable !== undefined)
46
+ field.nullable = raw.nullable;
47
+ if (raw.jsdoc !== undefined)
48
+ field.jsdoc = raw.jsdoc;
49
+ if (raw.default !== undefined)
50
+ field.default = raw.default;
51
+ if (raw.col !== undefined)
52
+ field.col = raw.col;
53
+ if (raw.literal !== undefined)
54
+ field.literal = raw.literal;
55
+ return field;
56
+ };
57
+ const OPERATION_TYPES = new Set(['query', 'mutation']);
58
+ const MUTATION_ACTIONS = new Set(['insert', 'update', 'upsert', 'delete']);
59
+ const AUTH_LEVELS = new Set(['NO_ACCESS', 'PUBLIC', 'USER']);
60
+ const API_KINDS = new Set(['callable', 'https', 'task', 'pubsub']);
61
+ const buildSelect = (entries) => entries.map(entry => ({
62
+ field: entry.field,
63
+ ...(entry.select !== undefined ? { select: buildSelect(entry.select) } : {}),
64
+ ...(entry.description !== undefined ? { description: entry.description } : {}),
65
+ ...(entry.alias !== undefined ? { alias: entry.alias } : {}),
66
+ ...(entry.args !== undefined ? { args: entry.args } : {}),
67
+ }));
68
+ const buildOperation = (name, raw, sourceFile) => {
69
+ const op = {
70
+ name,
71
+ operationType: (OPERATION_TYPES.has(raw.type) ? raw.type : 'query'),
72
+ model: raw.model,
73
+ auth: (raw.auth && AUTH_LEVELS.has(raw.auth) ? raw.auth : 'NO_ACCESS'),
74
+ inputs: raw.inputs ?? [],
75
+ where: raw.where ?? [],
76
+ select: buildSelect(raw.select ?? []),
77
+ orderBy: (raw.orderBy ?? []).map(o => ({ field: o.field, dir: o.dir === 'DESC' ? 'DESC' : 'ASC' })),
78
+ exprs: raw.exprs ?? [],
79
+ inc: raw.inc ?? [],
80
+ connectors: raw.connectors ?? [],
81
+ sourceFile,
82
+ };
83
+ if (raw.action !== undefined && MUTATION_ACTIONS.has(raw.action))
84
+ op.action = raw.action;
85
+ if (raw.authReason !== undefined)
86
+ op.authReason = raw.authReason;
87
+ if (raw.description !== undefined)
88
+ op.description = raw.description;
89
+ if (raw.limit !== undefined)
90
+ op.limit = raw.limit;
91
+ if (raw.single !== undefined && (raw.single === 'key' || raw.single === 'id'))
92
+ op.single = raw.single;
93
+ if (raw.keyArg !== undefined && (raw.keyArg === 'key' || raw.keyArg === 'id'))
94
+ op.keyArg = raw.keyArg;
95
+ if (raw.keyVars !== undefined)
96
+ op.keyVars = { ...raw.keyVars };
97
+ if (raw.entityDir !== undefined)
98
+ op.entityDir = raw.entityDir;
99
+ if (raw.whereAnd !== undefined)
100
+ op.whereAnd = raw.whereAnd;
101
+ if (raw.gqlName !== undefined)
102
+ op.gqlName = raw.gqlName;
103
+ if (raw.footer !== undefined)
104
+ op.footer = raw.footer;
105
+ if (raw.raw !== undefined)
106
+ op.raw = raw.raw;
107
+ if (raw.style !== undefined) {
108
+ op.style = {
109
+ ...(raw.style.signature === 'inline' || raw.style.signature === 'multi' ? { signature: raw.style.signature } : {}),
110
+ ...(raw.style.args === 'inline' || raw.style.args === 'multi' ? { args: raw.style.args } : {}),
111
+ ...(raw.style.data === 'inline' || raw.style.data === 'multi' || raw.style.data === 'compact' ? { data: raw.style.data } : {}),
112
+ ...(raw.style.orderBy === 'bare' || raw.style.orderBy === 'array' ? { orderBy: raw.style.orderBy } : {}),
113
+ ...(raw.style.auth === 'inline' || raw.style.auth === 'newline' ? { auth: raw.style.auth } : {}),
114
+ ...(raw.style.key === 'inline' || raw.style.key === 'multi' ? { key: raw.style.key } : {}),
115
+ ...(raw.style.and === 'inline' || raw.style.and === 'multi' ? { and: raw.style.and } : {}),
116
+ ...(raw.style.where === 'inline' || raw.style.where === 'multi' ? { where: raw.style.where } : {}),
117
+ };
118
+ }
119
+ if (raw.aggregate !== undefined)
120
+ op.aggregate = { count: raw.aggregate.count ?? false, sum: raw.aggregate.sum ?? [] };
121
+ return op;
122
+ };
123
+ const buildApiPayload = (raw, enumNames, modelNames) => {
124
+ const fields = raw?.fields
125
+ ? Object.entries(raw.fields).map(([fieldName, rawField]) => buildField(fieldName, rawField, enumNames, modelNames))
126
+ : [];
127
+ const payload = { fields, isVoid: raw?.void ?? false };
128
+ if (raw?.model !== undefined)
129
+ payload.model = raw.model;
130
+ return payload;
131
+ };
132
+ const buildApi = (name, raw, sourceFile, enumNames, modelNames) => {
133
+ const api = {
134
+ name,
135
+ kind: (API_KINDS.has(raw.kind) ? raw.kind : 'callable'),
136
+ request: buildApiPayload(raw.request, enumNames, modelNames),
137
+ response: buildApiPayload(raw.response, enumNames, modelNames),
138
+ sourceFile,
139
+ };
140
+ if (raw.method !== undefined)
141
+ api.method = raw.method;
142
+ if (raw.description !== undefined)
143
+ api.description = raw.description;
144
+ if (raw.envelope !== undefined ||
145
+ raw.maxAttempts !== undefined ||
146
+ raw.timeoutSeconds !== undefined ||
147
+ raw.topic !== undefined) {
148
+ api.task = {
149
+ ...(raw.envelope !== undefined ? { envelope: raw.envelope } : {}),
150
+ ...(raw.maxAttempts !== undefined ? { maxAttempts: raw.maxAttempts } : {}),
151
+ ...(raw.timeoutSeconds !== undefined ? { timeoutSeconds: raw.timeoutSeconds } : {}),
152
+ ...(raw.topic !== undefined ? { topic: raw.topic } : {}),
153
+ };
154
+ }
155
+ return api;
156
+ };
157
+ const buildFirestoreDoc = (name, raw, sourceFile, enumNames, modelNames) => {
158
+ const fields = raw.fields
159
+ ? Object.entries(raw.fields).map(([fieldName, rawField]) => buildField(fieldName, rawField, enumNames, modelNames))
160
+ : [];
161
+ const doc = {
162
+ name,
163
+ pick: raw.pick ?? [],
164
+ omit: raw.omit ?? [],
165
+ fields,
166
+ meta: raw.meta ?? true,
167
+ sourceFile,
168
+ };
169
+ if (raw.from !== undefined)
170
+ doc.from = raw.from;
171
+ if (raw.collection !== undefined)
172
+ doc.collection = raw.collection;
173
+ if (raw.description !== undefined)
174
+ doc.description = raw.description;
175
+ if (raw.helpers !== undefined)
176
+ doc.helpers = raw.helpers;
177
+ if (raw.include !== undefined)
178
+ doc.include = [...raw.include];
179
+ return doc;
180
+ };
181
+ /**
182
+ * Merge every resolved document into one normalized {@link Ir}.
183
+ *
184
+ * Type names on fields are resolved against the full set of enums and models
185
+ * gathered from all documents, so imports work transparently. Names defined in
186
+ * more than one place are reported as `DUPLICATE_DEFINITION` and the first
187
+ * definition wins. Unknown type names are left as `unresolved` refs for the
188
+ * validation step to flag.
189
+ */
190
+ export const buildIr = (documents) => {
191
+ const diagnostics = [];
192
+ const enumSources = new Map();
193
+ const modelSources = new Map();
194
+ // First pass: collect names (needed to resolve refs regardless of order).
195
+ for (const doc of documents) {
196
+ for (const name of Object.keys(doc.enums)) {
197
+ if (enumSources.has(name)) {
198
+ diagnostics.push(error('DUPLICATE_DEFINITION', `Enum "${name}" is defined more than once`, {
199
+ file: doc.filePath,
200
+ path: `enums.${name}`,
201
+ }));
202
+ continue;
203
+ }
204
+ enumSources.set(name, doc);
205
+ }
206
+ for (const name of Object.keys(doc.models)) {
207
+ if (modelSources.has(name)) {
208
+ diagnostics.push(error('DUPLICATE_DEFINITION', `Model "${name}" is defined more than once`, {
209
+ file: doc.filePath,
210
+ path: `models.${name}`,
211
+ }));
212
+ continue;
213
+ }
214
+ modelSources.set(name, doc);
215
+ }
216
+ }
217
+ const enumNames = new Set(enumSources.keys());
218
+ const modelNames = new Set(modelSources.keys());
219
+ const enums = [];
220
+ for (const [name, doc] of enumSources) {
221
+ const raw = doc.enums[name];
222
+ const irEnum = { name, values: [...raw.values], sourceFile: doc.filePath };
223
+ if (raw.description !== undefined)
224
+ irEnum.description = raw.description;
225
+ if (raw.valueComments !== undefined)
226
+ irEnum.valueComments = { ...raw.valueComments };
227
+ if (raw.valueKeys !== undefined)
228
+ irEnum.valueKeys = { ...raw.valueKeys };
229
+ if (raw.gqlName !== undefined)
230
+ irEnum.gqlName = raw.gqlName;
231
+ if (raw.fsName !== undefined)
232
+ irEnum.fsName = raw.fsName;
233
+ if (raw.fsDescription !== undefined)
234
+ irEnum.fsDescription = raw.fsDescription;
235
+ enums.push(irEnum);
236
+ }
237
+ const models = [];
238
+ for (const [name, doc] of modelSources) {
239
+ const raw = doc.models[name];
240
+ const fields = Object.entries(raw.fields).map(([fieldName, rawField]) => buildField(fieldName, rawField, enumNames, modelNames));
241
+ const model = {
242
+ name,
243
+ fields,
244
+ key: raw.key ?? [],
245
+ indexes: (raw.indexes ?? []).map(index => ({
246
+ fields: index.fields,
247
+ ...(index.name !== undefined ? { name: index.name } : {}),
248
+ unique: index.unique ?? false,
249
+ ...(index.expand !== undefined ? { expand: index.expand } : {}),
250
+ })),
251
+ sourceFile: doc.filePath,
252
+ };
253
+ if (raw.description !== undefined)
254
+ model.description = raw.description;
255
+ if (raw.table !== undefined)
256
+ model.table = raw.table;
257
+ if (raw.gqlName !== undefined)
258
+ model.gqlName = raw.gqlName;
259
+ if (raw.directives === 'inline' || raw.directives === 'multi')
260
+ model.directives = raw.directives;
261
+ if (raw.footer !== undefined)
262
+ model.footer = raw.footer;
263
+ if (raw.fsName !== undefined)
264
+ model.fsName = raw.fsName;
265
+ if (raw.sql !== undefined) {
266
+ model.sql = {
267
+ checks: raw.sql.checks ?? [],
268
+ foreignKeys: (raw.sql.foreignKeys ?? []).map(fk => ({
269
+ columns: fk.columns,
270
+ references: fk.references,
271
+ ...(fk.name !== undefined ? { name: fk.name } : {}),
272
+ })),
273
+ indexes: (raw.sql.indexes ?? []).map(idx => ({
274
+ columns: idx.columns,
275
+ ...(idx.name !== undefined ? { name: idx.name } : {}),
276
+ unique: idx.unique ?? false,
277
+ })),
278
+ };
279
+ }
280
+ models.push(model);
281
+ }
282
+ // Operation names are namespaced per connector (a real Data Connect layout has
283
+ // e.g. GetUserById in every connector, with different shapes). A duplicate is
284
+ // only an error when the same name targets an overlapping connector set.
285
+ const operationEntries = [];
286
+ const connectorsOf = (doc, name) => {
287
+ const cs = doc.operations[name].connectors ?? [];
288
+ return cs.length > 0 ? cs : [''];
289
+ };
290
+ const apiSources = new Map();
291
+ for (const doc of documents) {
292
+ for (const name of Object.keys(doc.operations)) {
293
+ const incoming = connectorsOf(doc, name);
294
+ const clash = operationEntries.find(entry => entry.name === name && connectorsOf(entry.doc, name).some(c => incoming.includes(c)));
295
+ if (clash) {
296
+ diagnostics.push(error('DUPLICATE_DEFINITION', `Operation "${name}" is defined more than once for the same connector`, {
297
+ file: doc.filePath,
298
+ path: `operations.${name}`,
299
+ }));
300
+ continue;
301
+ }
302
+ operationEntries.push({ name, doc });
303
+ }
304
+ for (const name of Object.keys(doc.apis)) {
305
+ if (apiSources.has(name)) {
306
+ diagnostics.push(error('DUPLICATE_DEFINITION', `Api "${name}" is defined more than once`, {
307
+ file: doc.filePath,
308
+ path: `apis.${name}`,
309
+ }));
310
+ continue;
311
+ }
312
+ apiSources.set(name, doc);
313
+ }
314
+ }
315
+ const operations = [];
316
+ for (const { name, doc } of operationEntries) {
317
+ operations.push(buildOperation(name, doc.operations[name], doc.filePath));
318
+ }
319
+ const apis = [];
320
+ for (const [name, doc] of apiSources) {
321
+ apis.push(buildApi(name, doc.apis[name], doc.filePath, enumNames, modelNames));
322
+ }
323
+ const unionSources = new Map();
324
+ for (const doc of documents) {
325
+ for (const name of Object.keys(doc.unions)) {
326
+ if (unionSources.has(name)) {
327
+ diagnostics.push(error('DUPLICATE_DEFINITION', `Union "${name}" is defined more than once`, {
328
+ file: doc.filePath,
329
+ path: `unions.${name}`,
330
+ }));
331
+ continue;
332
+ }
333
+ unionSources.set(name, doc);
334
+ }
335
+ }
336
+ const unions = [];
337
+ for (const [name, doc] of unionSources) {
338
+ const raw = doc.unions[name];
339
+ const union = {
340
+ name,
341
+ discriminant: raw.discriminant,
342
+ variants: raw.variants,
343
+ sourceFile: doc.filePath,
344
+ };
345
+ if (raw.description !== undefined)
346
+ union.description = raw.description;
347
+ unions.push(union);
348
+ }
349
+ const firestoreSources = new Map();
350
+ for (const doc of documents) {
351
+ for (const name of Object.keys(doc.firestore)) {
352
+ if (firestoreSources.has(name)) {
353
+ diagnostics.push(error('DUPLICATE_DEFINITION', `Firestore doc "${name}" is defined more than once`, {
354
+ file: doc.filePath,
355
+ path: `firestore.${name}`,
356
+ }));
357
+ continue;
358
+ }
359
+ firestoreSources.set(name, doc);
360
+ }
361
+ }
362
+ const firestore = [];
363
+ for (const [name, doc] of firestoreSources) {
364
+ firestore.push(buildFirestoreDoc(name, doc.firestore[name], doc.filePath, enumNames, modelNames));
365
+ }
366
+ const version = documents.length > 0 ? documents[documents.length - 1].version : 1;
367
+ const envelopeSources = new Map();
368
+ for (const doc of documents) {
369
+ for (const name of Object.keys(doc.envelopes)) {
370
+ if (envelopeSources.has(name)) {
371
+ diagnostics.push(error('DUPLICATE_DEFINITION', `Envelope "${name}" is defined more than once`, {
372
+ file: doc.filePath,
373
+ path: `envelopes.${name}`,
374
+ }));
375
+ continue;
376
+ }
377
+ envelopeSources.set(name, doc);
378
+ }
379
+ }
380
+ const envelopes = [];
381
+ for (const [name, doc] of envelopeSources) {
382
+ const raw = doc.envelopes[name];
383
+ const envelope = {
384
+ name,
385
+ fields: Object.entries(raw.fields).map(([fieldName, rawField]) => buildField(fieldName, rawField, enumNames, modelNames)),
386
+ sourceFile: doc.filePath,
387
+ };
388
+ if (raw.description !== undefined)
389
+ envelope.description = raw.description;
390
+ envelopes.push(envelope);
391
+ }
392
+ let header;
393
+ for (const doc of documents) {
394
+ if (doc.header !== undefined) {
395
+ header = doc.header;
396
+ break;
397
+ }
398
+ }
399
+ let project;
400
+ for (const doc of documents) {
401
+ if (doc.project) {
402
+ project = {
403
+ services: (doc.project.services ?? []).map(service => ({
404
+ name: service.name,
405
+ ...(service.database !== undefined ? { database: service.database } : {}),
406
+ ...(service.location !== undefined ? { location: service.location } : {}),
407
+ connectors: service.connectors ?? [],
408
+ })),
409
+ codebases: Object.entries(doc.project.codebases ?? {}).map(([codebase, service]) => ({ codebase, service })),
410
+ ...(doc.project.idCodec ? { idCodec: { ...doc.project.idCodec } } : {}),
411
+ };
412
+ break;
413
+ }
414
+ }
415
+ return {
416
+ ir: {
417
+ version,
418
+ enums,
419
+ models,
420
+ operations,
421
+ apis,
422
+ firestore,
423
+ unions,
424
+ envelopes,
425
+ ...(project ? { project } : {}),
426
+ ...(header !== undefined ? { header } : {}),
427
+ },
428
+ diagnostics,
429
+ };
430
+ };