qubu 0.0.0 → 0.3.4

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 (109) hide show
  1. package/dist/canonical-BbnqavJm.mjs +972 -0
  2. package/dist/codegen.d.mts +107 -0
  3. package/dist/codegen.mjs +1199 -0
  4. package/dist/column-CXMxx8Hq.mjs +118 -0
  5. package/dist/complete-D5Djh-zo.mjs +1788 -0
  6. package/dist/complete-types-BdFqUfbb.d.mts +371 -0
  7. package/dist/core.d.mts +33 -0
  8. package/dist/core.mjs +36 -0
  9. package/dist/ddl.d.mts +118 -0
  10. package/dist/ddl.mjs +1120 -0
  11. package/dist/dialect-b2-Z6uBF.mjs +13 -0
  12. package/dist/diff.d.mts +2 -0
  13. package/dist/diff.mjs +883 -0
  14. package/dist/drizzle-mysql.d.mts +24 -0
  15. package/dist/drizzle-mysql.mjs +72 -0
  16. package/dist/drizzle-postgres.d.mts +24 -0
  17. package/dist/drizzle-postgres.mjs +73 -0
  18. package/dist/drizzle-sqlite.d.mts +24 -0
  19. package/dist/drizzle-sqlite.mjs +82 -0
  20. package/dist/drizzle.d.mts +13 -0
  21. package/dist/drizzle.mjs +2 -0
  22. package/dist/errors-BGCoLe_r.mjs +14 -0
  23. package/dist/errors-Dxv73YJu.mjs +26 -0
  24. package/dist/explain-CkIK13L_.mjs +98 -0
  25. package/dist/index-Ds7-mhJi.d.mts +146 -0
  26. package/dist/index-Dug5HnLB.d.mts +204 -0
  27. package/dist/index.d.mts +2 -0
  28. package/dist/index.mjs +1276 -0
  29. package/dist/introspection.d.mts +117 -0
  30. package/dist/introspection.mjs +4341 -0
  31. package/dist/json-CUZlv4HT.mjs +169 -0
  32. package/dist/migration.d.mts +215 -0
  33. package/dist/migration.mjs +1161 -0
  34. package/dist/mysql-DqkqXB6A.mjs +355 -0
  35. package/dist/mysql.d.mts +5 -0
  36. package/dist/mysql.mjs +50 -0
  37. package/dist/naming-QVCOnSj2.mjs +20 -0
  38. package/dist/omit-OxV58AwX.mjs +5 -0
  39. package/dist/on-conflict-BxnxubMb.mjs +143 -0
  40. package/dist/postgres-DEBBeh52.mjs +235 -0
  41. package/dist/postgres.d.mts +123 -0
  42. package/dist/postgres.mjs +52 -0
  43. package/dist/registry-BufIskVN.mjs +569 -0
  44. package/dist/relational-DCZrrNia.mjs +77 -0
  45. package/dist/runtime-BTr-MTlo.mjs +193 -0
  46. package/dist/schema.d.mts +2 -0
  47. package/dist/schema.mjs +5 -0
  48. package/dist/serialize-PF1cfH2P.mjs +556 -0
  49. package/dist/snapshot-CWPgzxNx.mjs +392 -0
  50. package/dist/snapshot.d.mts +4 -0
  51. package/dist/snapshot.mjs +8 -0
  52. package/dist/source-DUoJVXmL.mjs +68 -0
  53. package/dist/sqlite-BU6DBxef.mjs +320 -0
  54. package/dist/sqlite.d.mts +5 -0
  55. package/dist/sqlite.mjs +48 -0
  56. package/dist/standard-BTVYKh_F.mjs +12 -0
  57. package/dist/table-llv9tsZ8.mjs +384 -0
  58. package/dist/types-4Q076HKo.d.mts +3636 -0
  59. package/dist/types-BX0mckiU.d.mts +644 -0
  60. package/dist/types-Cec0xzo4.mjs +116 -0
  61. package/dist/types-H4vyCw8_.d.mts +45 -0
  62. package/dist/value-BvilP0oz.mjs +29 -0
  63. package/dist/vite/ambient.d.ts +449 -0
  64. package/dist/vite.d.mts +35 -0
  65. package/dist/vite.mjs +498 -0
  66. package/docs/config.json +21 -0
  67. package/docs/dialects-and-execution.md +427 -0
  68. package/docs/getting-started.md +96 -0
  69. package/docs/guides/compose-queries.md +225 -0
  70. package/docs/guides/drizzle.md +130 -0
  71. package/docs/guides/extensions/dialects.md +40 -0
  72. package/docs/guides/extensions/overview.md +36 -0
  73. package/docs/guides/extensions/sources-and-clauses.md +82 -0
  74. package/docs/guides/extensions/typed-expressions.md +94 -0
  75. package/docs/guides/extensions/unsafe-syntax.md +27 -0
  76. package/docs/guides/json.md +65 -0
  77. package/docs/guides/mutations.md +138 -0
  78. package/docs/guides/select/conditions.md +110 -0
  79. package/docs/guides/select/grouping-and-windows.md +82 -0
  80. package/docs/guides/select/ordering-and-pagination.md +78 -0
  81. package/docs/guides/select/overview.md +139 -0
  82. package/docs/guides/sql-templates.md +157 -0
  83. package/docs/guides/vite-plugin.md +92 -0
  84. package/docs/index.md +111 -0
  85. package/docs/query-model/fragments.md +109 -0
  86. package/docs/query-model/result-shapes.md +125 -0
  87. package/docs/query-model/source-scope.md +182 -0
  88. package/docs/reference/introspection-support.md +196 -0
  89. package/docs/reference/mysql-snapshot.md +55 -0
  90. package/docs/reference/postgres-snapshot.md +61 -0
  91. package/docs/reference/sqlite-snapshot.md +53 -0
  92. package/docs/reference/supported-surface.md +104 -0
  93. package/docs/schema/catalog-model.md +58 -0
  94. package/docs/schema/code-generation.md +139 -0
  95. package/docs/schema/columns-and-writes.md +130 -0
  96. package/docs/schema/constraints-and-indexes.md +180 -0
  97. package/docs/schema/ddl-emission.md +75 -0
  98. package/docs/schema/diff.md +75 -0
  99. package/docs/schema/introspection.md +214 -0
  100. package/docs/schema/migration-plans.md +76 -0
  101. package/docs/schema/snapshots.md +93 -0
  102. package/docs/schema/storage-and-schema-sql.md +104 -0
  103. package/docs/schema/tables-and-names.md +106 -0
  104. package/docs/sql-semantic-types.md +141 -0
  105. package/docs/troubleshooting.md +151 -0
  106. package/package.json +132 -10
  107. package/skills/qubu/SKILL.md +42 -0
  108. package/skills/qubu/agents/openai.yaml +6 -0
  109. package/readme.md +0 -1
@@ -0,0 +1,1199 @@
1
+ import { l as decodeSchemaSnapshot } from "./canonical-BbnqavJm.mjs";
2
+ import { t as mapCatalogToSnapshot } from "./snapshot-CWPgzxNx.mjs";
3
+ //#region src/codegen/source.ts
4
+ const header = `/* Generated by Qubu. Do not edit this machine-owned file. */
5
+ import * as _qubu from 'qubu'
6
+ `;
7
+ const catalogColumnHelper = `type _CatalogColumnOptions = Omit<_qubu.ColumnOptions, 'storage'> & {
8
+ readonly storage?: never
9
+ }
10
+
11
+ const _catalogColumn = <
12
+ TOutput = unknown,
13
+ TInsert = unknown,
14
+ TUpdate = unknown,
15
+ TSqlType extends _qubu.AnySqlType = _qubu.SqlUnknown,
16
+ >() =>
17
+ <
18
+ const TDialect extends string,
19
+ const TDeclaration extends string,
20
+ const TOptions extends _CatalogColumnOptions,
21
+ >(
22
+ dialect: TDialect,
23
+ declaration: TDeclaration,
24
+ options: TOptions
25
+ ) =>
26
+ _qubu.nativeColumn<
27
+ TOutput,
28
+ TInsert,
29
+ TUpdate,
30
+ TOptions,
31
+ TSqlType,
32
+ TDialect,
33
+ TDeclaration
34
+ >(_qubu.nativeStorage(dialect, declaration), options)
35
+ `;
36
+ const catalogPredicateHelper = `const _catalogPredicate = <
37
+ const TDialect extends _qubu.SchemaDialectName,
38
+ >(input: _qubu.CatalogCheckSql<TDialect>) =>
39
+ _qubu.catalogCheck(input).expression
40
+ `;
41
+ function printSchemaSource(schema, diagnostics) {
42
+ const tables = schema.tables.map((table) => printTable(schema, table, diagnostics));
43
+ if (tables.some((table) => table === void 0)) return void 0;
44
+ const registry = schema.tables.map((table) => ` ${literal(table.name)}: ${table.name},`).join("\n");
45
+ const predicateHelper = schema.tables.some((table) => table.indexes.some((index) => index.snapshot.predicate !== void 0)) ? `\n${catalogPredicateHelper}` : "";
46
+ const columnHelper = schema.tables.some((table) => table.columns.length > 0) ? `\n${catalogColumnHelper}` : "";
47
+ return `${header}${columnHelper}${predicateHelper}\n${tables.join("\n\n")}\n\nexport const ${schema.name} = _qubu.schema(\n {\n${registry}\n },\n { namespace: ${literal(schema.namespace)} }\n)\n`;
48
+ }
49
+ function printTable(schema, table, diagnostics) {
50
+ const columns = table.columns.map((column) => printColumn(schema, table, column, diagnostics));
51
+ const constraints = table.constraints.map((constraint) => printConstraint(schema, table, constraint, diagnostics));
52
+ const indexes = table.indexes.map((index) => printIndex(schema, table, index, diagnostics));
53
+ if (columns.some((value) => value === void 0) || constraints.some((value) => value === void 0) || indexes.some((value) => value === void 0)) return;
54
+ const typePrefix = `_${capitalize(table.name)}`;
55
+ const definitionsName = `${typePrefix}Definitions`;
56
+ const columnsName = `${typePrefix}Columns`;
57
+ const constraintsName = `${typePrefix}Constraints`;
58
+ const indexesName = `${typePrefix}Indexes`;
59
+ const tableTypeName = `${typePrefix}Table`;
60
+ const constraintTypes = table.constraints.map((constraint) => printConstraintType(table, constraint, columnsName, diagnostics));
61
+ const indexTypes = table.indexes.map((index) => printIndexType(table, index, columnsName, diagnostics));
62
+ if (constraintTypes.some((value) => value === void 0) || indexTypes.some((value) => value === void 0)) return;
63
+ return `const ${definitionsName} = {
64
+ ${columns.map((value) => indent(value ?? "", 2)).join("\n")}
65
+ }
66
+
67
+ type ${columnsName} = _qubu.TableColumns<
68
+ typeof ${definitionsName},
69
+ _qubu.TableIdentity<${literal(table.snapshot.physicalName)}>
70
+ >
71
+
72
+ type ${constraintsName} = {
73
+ ${constraintTypes.map((value) => indent(value ?? "", 2)).join("\n")}
74
+ }
75
+
76
+ type ${indexesName} = {
77
+ ${indexTypes.map((value) => indent(value ?? "", 2)).join("\n")}
78
+ }
79
+
80
+ type ${tableTypeName} = _qubu.Table<
81
+ ${literal(table.snapshot.physicalName)},
82
+ typeof ${definitionsName},
83
+ ${constraintsName},
84
+ ${indexesName}
85
+ > & {
86
+ readonly columns: ${columnsName}
87
+ }
88
+
89
+ export const ${table.name}: ${tableTypeName} = _qubu.table(
90
+ ${literal(table.snapshot.physicalName)},
91
+ ${definitionsName},
92
+ _current => ({
93
+ constraints: {
94
+ ${constraints.map((value) => indent(value ?? "", 6)).join("\n")}
95
+ },
96
+ indexes: {
97
+ ${indexes.map((value) => indent(value ?? "", 6)).join("\n")}
98
+ },
99
+ })
100
+ )`;
101
+ }
102
+ function printConstraintType(table, constraint, columnsName, diagnostics) {
103
+ const snapshot = constraint.snapshot;
104
+ const path = [
105
+ "snapshot",
106
+ "tables",
107
+ table.snapshot.id,
108
+ "constraints",
109
+ snapshot.id
110
+ ];
111
+ if (snapshot.kind === "check") return `readonly ${literal(constraint.name)}: _qubu.CheckConstraint`;
112
+ const columns = printColumnTypeTuple(table, snapshot.columns, columnsName, path, diagnostics);
113
+ if (columns === void 0) return void 0;
114
+ if (snapshot.kind === "foreign-key") return `readonly ${literal(constraint.name)}: _qubu.ForeignKeyConstraint<${columns}>`;
115
+ if (snapshot.kind === "unique-constraint") return `readonly ${literal(constraint.name)}: _qubu.UniqueConstraint<
116
+ ${columns},
117
+ ${literal(snapshot.nulls)}
118
+ >`;
119
+ return `readonly ${literal(constraint.name)}: _qubu.KeyConstraint<
120
+ ${literal(snapshot.kind)},
121
+ ${columns}
122
+ >`;
123
+ }
124
+ function printIndexType(table, index, columnsName, diagnostics) {
125
+ const path = [
126
+ "snapshot",
127
+ "tables",
128
+ table.snapshot.id,
129
+ "indexes",
130
+ index.snapshot.id
131
+ ];
132
+ const terms = index.snapshot.terms.map((term, position) => printIndexTermType(table, term, columnsName, [
133
+ ...path,
134
+ "terms",
135
+ position
136
+ ], diagnostics));
137
+ if (terms.some((term) => term === void 0)) return void 0;
138
+ const properties = [
139
+ `readonly terms: readonly [${terms.join(", ")}]`,
140
+ `readonly unique: ${index.snapshot.unique ? "true" : "false"}`,
141
+ `readonly candidateKey: ${index.snapshot.candidateKey ? "true" : "false"}`,
142
+ `readonly predicate: ${index.snapshot.predicate === void 0 ? "undefined" : "_qubu.CatalogCheckExpression"}`
143
+ ];
144
+ if (index.snapshot.includedColumns !== void 0) {
145
+ const included = printColumnTypeTuple(table, index.snapshot.includedColumns, columnsName, [...path, "includedColumns"], diagnostics);
146
+ if (included === void 0) return void 0;
147
+ properties.push(`readonly includedColumns?: ${included}`);
148
+ } else properties.push("readonly includedColumns?: undefined");
149
+ return `readonly ${literal(index.name)}: {
150
+ readonly kind: 'index'
151
+ ${properties.map((property) => ` ${property}`).join("\n")}
152
+ }`;
153
+ }
154
+ function printIndexTermType(table, term, columnsName, path, diagnostics) {
155
+ if (term.kind !== "order") return printIndexTermExpressionType(table, term, columnsName, path, diagnostics);
156
+ const expression = printIndexTermExpressionType(table, term.expression, columnsName, [...path, "expression"], diagnostics);
157
+ return expression === void 0 ? void 0 : `_qubu.OrderTerm<unknown, ${expression}>`;
158
+ }
159
+ function printIndexTermExpressionType(table, expression, columnsName, path, diagnostics) {
160
+ if (expression.kind === "expression") return "_qubu.AnyExpression";
161
+ const column = table.columnsBySnapshotId.get(expression.column);
162
+ if (column === void 0) {
163
+ diagnostics.push(sourceError(`Index column "${expression.column}" was not resolved`, [...path, "column"]));
164
+ return;
165
+ }
166
+ return `${columnsName}[${literal(column.name)}]`;
167
+ }
168
+ function printColumnTypeTuple(table, ids, columnsName, path, diagnostics) {
169
+ const columns = [];
170
+ for (const [position, id] of ids.entries()) {
171
+ const column = table.columnsBySnapshotId.get(id);
172
+ if (column === void 0) {
173
+ diagnostics.push(sourceError(`Column "${id}" was not resolved`, [
174
+ ...path,
175
+ "columns",
176
+ position
177
+ ]));
178
+ continue;
179
+ }
180
+ columns.push(`${columnsName}[${literal(column.name)}]`);
181
+ }
182
+ return columns.length === ids.length && columns.length > 0 ? `readonly [${columns.join(", ")}]` : void 0;
183
+ }
184
+ function printColumn(schema, table, column, diagnostics) {
185
+ const path = [
186
+ "snapshot",
187
+ "tables",
188
+ table.snapshot.id,
189
+ "columns",
190
+ column.snapshot.id
191
+ ];
192
+ const snapshot = column.snapshot;
193
+ if (snapshot.storage?.kind !== "native") {
194
+ diagnostics.push(sourceError("Generated columns require native storage", [...path, "storage"]));
195
+ return;
196
+ }
197
+ const options = [`nullable: ${snapshot.nullable ? "true" : "false"}`, `sqlName: ${literal(snapshot.physicalName)}`];
198
+ if (snapshot.default !== void 0) {
199
+ if (snapshot.default.kind === "external") options.push("default: _qubu.externalDefault()");
200
+ else if (snapshot.default.kind === "literal") {
201
+ const value = printSnapshotLiteral(snapshot.default.value, path, diagnostics);
202
+ if (value === void 0) return void 0;
203
+ options.push(`default: ${value}`);
204
+ } else {
205
+ const value = printExpression(snapshot.default.expression, schema, [
206
+ ...path,
207
+ "default",
208
+ "expression"
209
+ ], diagnostics);
210
+ if (value === void 0) return void 0;
211
+ options.push(`default: ${value}`);
212
+ }
213
+ }
214
+ if (snapshot.generatedColumn !== void 0) {
215
+ if (snapshot.generatedColumn.kind === "external") options.push("generatedColumn: _qubu.externalGeneratedColumn()");
216
+ else {
217
+ const expression = printExpression(snapshot.generatedColumn.expression, schema, [
218
+ ...path,
219
+ "generatedColumn",
220
+ "expression"
221
+ ], diagnostics);
222
+ if (expression === void 0) return void 0;
223
+ options.push(`generatedColumn: _qubu.generatedColumn(${expression}, ${literal(snapshot.generatedColumn.mode)})`);
224
+ }
225
+ }
226
+ if (snapshot.identity !== void 0) {
227
+ const extension = snapshot.identity.dialect ? printExtension(snapshot.identity.dialect, schema, [
228
+ ...path,
229
+ "identity",
230
+ "dialect"
231
+ ], diagnostics) : void 0;
232
+ if (snapshot.identity.dialect !== void 0 && extension === void 0) return;
233
+ options.push(extension === void 0 ? `identity: _qubu.identityColumn(${literal(snapshot.identity.generation)})` : `identity: _qubu.identityColumn(${literal(snapshot.identity.generation)}, { dialect: ${extension} })`);
234
+ }
235
+ if (snapshot.onUpdate !== void 0) {
236
+ const expression = printExpression(snapshot.onUpdate, schema, [...path, "onUpdate"], diagnostics);
237
+ if (expression === void 0) return void 0;
238
+ options.push(`onUpdate: ${expression}`);
239
+ }
240
+ const types = [
241
+ applicationType(column.output),
242
+ applicationType(column.insert),
243
+ applicationType(column.update),
244
+ sqlDomainType(column.sqlDomain)
245
+ ].join(", ");
246
+ const optionSource = `{
247
+ ${options.map((option) => ` ${option},`).join("\n")}
248
+ }`;
249
+ return `${literal(column.name)}: _catalogColumn<${types}>()(
250
+ ${literal(snapshot.storage.dialect)},
251
+ ${literal(snapshot.storage.type)},
252
+ ${indent(optionSource, 2)}
253
+ ),`;
254
+ }
255
+ function printConstraint(schema, table, constraint, diagnostics) {
256
+ const snapshot = constraint.snapshot;
257
+ const path = [
258
+ "snapshot",
259
+ "tables",
260
+ table.snapshot.id,
261
+ "constraints",
262
+ snapshot.id
263
+ ];
264
+ const options = printConstraintOptions(snapshot, schema, path, diagnostics);
265
+ if (options === void 0) return void 0;
266
+ let expression;
267
+ if (snapshot.kind === "check") {
268
+ const sql = catalogExpressionData(snapshot.expression, schema, [...path, "expression"], diagnostics);
269
+ if (sql === void 0) return void 0;
270
+ expression = `_qubu.catalogCheck(${sql}, ${options})`;
271
+ } else if (snapshot.kind === "foreign-key") {
272
+ const local = printColumns(table, snapshot.columns, path, diagnostics);
273
+ const targetTable = schema.tablesBySnapshotId.get(snapshot.target.table);
274
+ if (targetTable === void 0) {
275
+ diagnostics.push(sourceError(`Foreign-key target table "${snapshot.target.table}" was not resolved`, [
276
+ ...path,
277
+ "target",
278
+ "table"
279
+ ]));
280
+ return;
281
+ }
282
+ const target = printColumns(targetTable, snapshot.target.columns, [...path, "target"], diagnostics, targetTable.name);
283
+ if (local === void 0 || target === void 0) return void 0;
284
+ expression = `_qubu.catalogForeignKey(
285
+ [${local.join(", ")}],
286
+ () => _qubu.references(${targetTable.name}, ${target.join(", ")}),
287
+ ${options}
288
+ )`;
289
+ } else {
290
+ const columns = printColumns(table, snapshot.columns, path, diagnostics);
291
+ if (columns === void 0) return void 0;
292
+ expression = `_qubu.${snapshot.kind === "primary-key" ? "primaryKey" : snapshot.kind === "unique" ? "unique" : "uniqueConstraint"}(${columns.join(", ")}, ${options})`;
293
+ }
294
+ return `${literal(constraint.name)}: ${expression},`;
295
+ }
296
+ function printConstraintOptions(constraint, schema, path, diagnostics) {
297
+ const properties = [`physicalName: ${literal(constraint.physicalName)}`];
298
+ if (constraint.kind === "unique-constraint") properties.push(`nulls: ${literal(constraint.nulls)}`);
299
+ if (constraint.kind === "foreign-key") {
300
+ if (constraint.onUpdate !== void 0) properties.push(`onUpdate: ${literal(constraint.onUpdate)}`);
301
+ if (constraint.onDelete !== void 0) properties.push(`onDelete: ${literal(constraint.onDelete)}`);
302
+ if (constraint.match !== void 0) properties.push(`match: ${literal(constraint.match)}`);
303
+ }
304
+ if (constraint.deferrable !== void 0) properties.push(`deferrable: ${constraint.deferrable ? "true" : "false"}`);
305
+ if (constraint.initially !== void 0) properties.push(`initially: ${literal(constraint.initially)}`);
306
+ if (constraint.dialect !== void 0) {
307
+ const extension = printExtension(constraint.dialect, schema, [...path, "dialect"], diagnostics);
308
+ if (extension === void 0) return void 0;
309
+ properties.push(`dialect: ${extension}`);
310
+ }
311
+ return `{ ${properties.join(", ")} }`;
312
+ }
313
+ function printIndex(schema, table, index, diagnostics) {
314
+ const path = [
315
+ "snapshot",
316
+ "tables",
317
+ table.snapshot.id,
318
+ "indexes",
319
+ index.snapshot.id
320
+ ];
321
+ const terms = index.snapshot.terms.map((term, termIndex) => printIndexTerm(schema, table, term, [
322
+ ...path,
323
+ "terms",
324
+ termIndex
325
+ ], diagnostics));
326
+ if (terms.some((term) => term === void 0)) return void 0;
327
+ const options = [`physicalName: ${literal(index.snapshot.physicalName)}`, `unique: ${index.snapshot.unique ? "true" : "false"}`];
328
+ if (index.snapshot.predicate !== void 0) {
329
+ const predicate = catalogExpressionData(index.snapshot.predicate, schema, [...path, "predicate"], diagnostics);
330
+ if (predicate === void 0) return void 0;
331
+ options.push(`where: _catalogPredicate(${predicate})`);
332
+ }
333
+ if (index.snapshot.includedColumns !== void 0) {
334
+ const included = printColumns(table, index.snapshot.includedColumns, [...path, "includedColumns"], diagnostics);
335
+ if (included === void 0) return void 0;
336
+ options.push(`include: [${included.join(", ")}]`);
337
+ }
338
+ if (index.snapshot.dialect !== void 0) {
339
+ const extension = printExtension(index.snapshot.dialect, schema, [...path, "dialect"], diagnostics);
340
+ if (extension === void 0) return void 0;
341
+ options.push(`dialect: ${extension}`);
342
+ }
343
+ return `${literal(index.name)}: _qubu.index(
344
+ [${terms.join(", ")}],
345
+ { ${options.join(", ")} }
346
+ ),`;
347
+ }
348
+ function printIndexTerm(schema, table, term, path, diagnostics) {
349
+ if (term.kind !== "order") return printIndexTermExpression(schema, table, term, path, diagnostics);
350
+ const expression = printIndexTermExpression(schema, table, term.expression, [...path, "expression"], diagnostics);
351
+ if (expression === void 0) return void 0;
352
+ return `_qubu.order(${expression}, ${term.direction === void 0 ? "undefined" : literal(term.direction)}, ${term.nulls === void 0 ? "undefined" : literal(term.nulls)})`;
353
+ }
354
+ function printIndexTermExpression(schema, table, expression, path, diagnostics) {
355
+ if (expression.kind === "expression") return printExpression(expression.expression, schema, [...path, "expression"], diagnostics);
356
+ const column = table.columnsBySnapshotId.get(expression.column);
357
+ if (column === void 0) {
358
+ diagnostics.push(sourceError(`Index column "${expression.column}" was not resolved`, [...path, "column"]));
359
+ return;
360
+ }
361
+ return `_current.columns[${literal(column.name)}]`;
362
+ }
363
+ function printColumns(table, ids, path, diagnostics, binding = "_current") {
364
+ const result = [];
365
+ for (const [index, id] of ids.entries()) {
366
+ const column = table.columnsBySnapshotId.get(id);
367
+ if (column === void 0) {
368
+ diagnostics.push(sourceError(`Column "${id}" was not resolved`, [
369
+ ...path,
370
+ "columns",
371
+ index
372
+ ]));
373
+ continue;
374
+ }
375
+ result.push(`${binding}.columns[${literal(column.name)}]`);
376
+ }
377
+ return result.length === ids.length && result.length > 0 ? result : void 0;
378
+ }
379
+ function printExpression(expression, schema, path, diagnostics) {
380
+ const data = catalogExpressionData(expression, schema, path, diagnostics);
381
+ return data === void 0 ? void 0 : `_qubu.unsafeSchemaSql(${data})`;
382
+ }
383
+ function catalogExpressionData(expression, schema, path, diagnostics) {
384
+ if (expression.expressionKind !== "unsafe" || expression.dialect !== schema.dialect || expression.sql.trim().length === 0) {
385
+ diagnostics.push(sourceError("Catalog expressions must be non-empty, unsafe, and tagged for the selected dialect", path));
386
+ return;
387
+ }
388
+ return `{ dialect: ${literal(expression.dialect)}, sql: ${literal(expression.sql)} }`;
389
+ }
390
+ function printExtension(extension, schema, path, diagnostics) {
391
+ if (extension.dialect !== schema.dialect || extension.version !== 1 || !isJsonObject(extension.data) || Object.hasOwn(extension.data, "dialect")) {
392
+ diagnostics.push(sourceError("Dialect extensions must be v1 object data for the selected dialect and cannot contain a data field named \"dialect\"", path));
393
+ return;
394
+ }
395
+ const data = extension.data;
396
+ const entries = Object.keys(data).sort().map((key) => `[${literal(key)}]: ${printJsonValue(data[key])}`);
397
+ return `{ dialect: ${literal(extension.dialect)}${entries.length === 0 ? "" : `, ${entries.join(", ")}`} }`;
398
+ }
399
+ function printSnapshotLiteral(value, path, diagnostics) {
400
+ switch (value.kind) {
401
+ case "null": return "null";
402
+ case "boolean": return value.value ? "true" : "false";
403
+ case "string": return literal(value.value);
404
+ case "bigint": return `BigInt(${literal(value.value)})`;
405
+ case "number": {
406
+ const number = Number(value.value);
407
+ if (!Number.isFinite(number)) {
408
+ diagnostics.push(sourceError("Default number literals must be finite", [...path, "default"]));
409
+ return;
410
+ }
411
+ return Object.is(number, -0) ? "0" : String(number);
412
+ }
413
+ }
414
+ }
415
+ function printJsonValue(value) {
416
+ if (value === null) return "null";
417
+ if (typeof value === "string") return literal(value);
418
+ if (typeof value === "boolean") return value ? "true" : "false";
419
+ if (typeof value === "number") return Object.is(value, -0) ? "0" : String(value);
420
+ if (Array.isArray(value)) return `[${value.map(printJsonValue).join(", ")}]`;
421
+ return `{ ${Object.keys(value).sort().map((key) => `[${literal(key)}]: ${printJsonValue(value[key])}`).join(", ")} }`;
422
+ }
423
+ function applicationType(value) {
424
+ return value;
425
+ }
426
+ function sqlDomainType(value) {
427
+ switch (value) {
428
+ case "unknown": return "_qubu.SqlUnknown";
429
+ case "integer": return "_qubu.SqlInteger";
430
+ case "decimal": return "_qubu.SqlDecimal";
431
+ case "text": return "_qubu.SqlText";
432
+ case "boolean": return "_qubu.SqlBoolean";
433
+ case "date": return "_qubu.SqlDate";
434
+ case "timestamp": return "_qubu.SqlTimestamp";
435
+ case "uuid": return "_qubu.SqlUuid";
436
+ case "json": return "_qubu.SqlJson";
437
+ case "bigint": return "_qubu.SqlBigInt";
438
+ case "binary": return "_qubu.SqlBinary";
439
+ }
440
+ }
441
+ function sourceError(message, path) {
442
+ return {
443
+ severity: "error",
444
+ code: "unrepresentable-fact",
445
+ message,
446
+ path
447
+ };
448
+ }
449
+ function literal(value) {
450
+ return JSON.stringify(value).replaceAll("\u2028", "\\u2028").replaceAll("\u2029", "\\u2029");
451
+ }
452
+ function capitalize(value) {
453
+ return `${value.slice(0, 1).toUpperCase()}${value.slice(1)}`;
454
+ }
455
+ function indent(value, spaces) {
456
+ const prefix = " ".repeat(spaces);
457
+ return value.split("\n").map((line) => `${prefix}${line}`).join("\n");
458
+ }
459
+ function isJsonObject(value) {
460
+ return typeof value === "object" && value !== null && !Array.isArray(value);
461
+ }
462
+ //#endregion
463
+ //#region src/codegen/generate.ts
464
+ const supportedDialects = /* @__PURE__ */ new Set([
465
+ "postgresql",
466
+ "sqlite",
467
+ "mysql"
468
+ ]);
469
+ const applicationTypes = /* @__PURE__ */ new Set([
470
+ "unknown",
471
+ "string",
472
+ "number",
473
+ "boolean",
474
+ "bigint",
475
+ "Date",
476
+ "Uint8Array"
477
+ ]);
478
+ const sqlDomains = /* @__PURE__ */ new Set([
479
+ "unknown",
480
+ "integer",
481
+ "decimal",
482
+ "text",
483
+ "boolean",
484
+ "date",
485
+ "timestamp",
486
+ "uuid",
487
+ "json",
488
+ "bigint",
489
+ "binary"
490
+ ]);
491
+ const orderableDomains = /* @__PURE__ */ new Set([
492
+ "unknown",
493
+ "integer",
494
+ "decimal",
495
+ "text",
496
+ "date",
497
+ "timestamp",
498
+ "bigint"
499
+ ]);
500
+ const reservedBindings = /* @__PURE__ */ new Set([
501
+ "arguments",
502
+ "await",
503
+ "break",
504
+ "case",
505
+ "catch",
506
+ "class",
507
+ "const",
508
+ "continue",
509
+ "debugger",
510
+ "default",
511
+ "delete",
512
+ "do",
513
+ "else",
514
+ "enum",
515
+ "eval",
516
+ "export",
517
+ "extends",
518
+ "false",
519
+ "finally",
520
+ "for",
521
+ "function",
522
+ "if",
523
+ "implements",
524
+ "import",
525
+ "in",
526
+ "interface",
527
+ "instanceof",
528
+ "let",
529
+ "new",
530
+ "null",
531
+ "package",
532
+ "private",
533
+ "protected",
534
+ "public",
535
+ "return",
536
+ "static",
537
+ "super",
538
+ "switch",
539
+ "this",
540
+ "throw",
541
+ "true",
542
+ "try",
543
+ "typeof",
544
+ "var",
545
+ "void",
546
+ "while",
547
+ "with",
548
+ "yield"
549
+ ]);
550
+ const excludedFamilies = [
551
+ ["views", "views"],
552
+ ["sequences", "sequences"],
553
+ ["enums", "enums"],
554
+ ["domains", "domains"],
555
+ ["collations", "collations"],
556
+ ["triggers", "triggers"],
557
+ ["routines", "routines"],
558
+ ["partitions", "partitions"],
559
+ ["policies", "policies"],
560
+ ["extensionObjects", "extension objects"],
561
+ ["deferredObjects", "deferred objects"],
562
+ ["opaqueObjects", "opaque objects"],
563
+ ["comments", "comments"],
564
+ ["ownership", "ownership records"]
565
+ ];
566
+ /**
567
+ * Generate a deterministic TypeScript module from one introspection result.
568
+ *
569
+ * @remarks
570
+ * Generation accepts only a successful, non-lossy Snapshot v1 result for one
571
+ * PostgreSQL, SQLite, or MySQL namespace. It is pure: it opens no connection,
572
+ * performs no filesystem writes, and never evaluates catalog SQL. Existing
573
+ * introspection diagnostics are copied into the result. Any error returns no
574
+ * source.
575
+ *
576
+ * The generated module exports every ordinary table plus one schema registry.
577
+ * Physical names and native declarations remain escaped string literals.
578
+ * Catalog expressions use dialect-tagged schema data, checks use
579
+ * `catalogCheck()`, and foreign keys use lazy `catalogForeignKey()` targets.
580
+ * Foreign keys must have equal local and target arity and target an exact
581
+ * primary key, strict unique key, or candidate index. Nullable database UNIQUE
582
+ * constraints do not provide that candidate-key proof and fail generation.
583
+ *
584
+ * Application output, insert, and update types default independently to
585
+ * `unknown`. The generator may attach a conservative SQL semantic domain, and
586
+ * {@link SchemaCodegenOptions.mapColumn} can override either type axis with a
587
+ * fixed token. Naming callbacks can replace suggested camelCase IDs, but they
588
+ * cannot supply source syntax.
589
+ *
590
+ * @param input A completed introspection operation. Failed and lossy results
591
+ * are reported as diagnostics rather than thrown errors.
592
+ * @param options Controlled logical-name and column-type mappings.
593
+ * @returns Generated source and diagnostics, or diagnostics without source.
594
+ */
595
+ function generateSchemaSource(input, options = {}) {
596
+ const diagnostics = [];
597
+ try {
598
+ if (!isOptions(options)) {
599
+ diagnostics.push(errorDiagnostic("invalid-option", "Code generation options must be an object", ["options"]));
600
+ return failure(diagnostics);
601
+ }
602
+ if (!isIntrospectionResult(input)) {
603
+ diagnostics.push(errorDiagnostic("invalid-input", "Code generation requires an introspection result", []));
604
+ return failure(diagnostics);
605
+ }
606
+ diagnostics.push(...input.diagnostics);
607
+ appendExcludedFamilyDiagnostics(input.catalog, diagnostics);
608
+ if (!input.ok) {
609
+ diagnostics.push(errorDiagnostic("invalid-input", "Code generation requires a successful introspection result", []));
610
+ return failure(diagnostics);
611
+ }
612
+ if (input.lossy) diagnostics.push(errorDiagnostic("lossy-input", "Code generation rejects lossy introspection results", ["lossy"], "Run introspection in strict mode and resolve every reported omission."));
613
+ if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) return failure(diagnostics);
614
+ const decoded = decodeSchemaSnapshot(input.snapshot);
615
+ if (!decoded.ok) {
616
+ diagnostics.push(...decoded.diagnostics.map((diagnostic) => errorDiagnostic("unsupported-snapshot", diagnostic.message, diagnostic.path)));
617
+ return failure(diagnostics);
618
+ }
619
+ const snapshot = decoded.value;
620
+ validateInputEnvelope(input.catalog, snapshot, diagnostics);
621
+ validatePhysicalFacts(input.catalog, snapshot, diagnostics);
622
+ validateSchemaProofs(snapshot, diagnostics);
623
+ if (hasErrors(diagnostics)) return failure(diagnostics);
624
+ const resolved = resolveSchema(input.catalog, snapshot, options, diagnostics);
625
+ if (resolved === void 0 || hasErrors(diagnostics)) return failure(diagnostics);
626
+ validateResolvedDomains(resolved, diagnostics);
627
+ if (hasErrors(diagnostics)) return failure(diagnostics);
628
+ const source = printSchemaSource(resolved, diagnostics);
629
+ if (source === void 0 || hasErrors(diagnostics)) return failure(diagnostics);
630
+ return Object.freeze({
631
+ ok: true,
632
+ source,
633
+ diagnostics: freezeDiagnostics(diagnostics)
634
+ });
635
+ } catch (caught) {
636
+ diagnostics.push(errorDiagnostic("unsafe-source", caught instanceof Error ? `Schema source generation failed safely: ${caught.message}` : "Schema source generation failed safely", []));
637
+ return failure(diagnostics);
638
+ }
639
+ }
640
+ function validateInputEnvelope(catalog, snapshot, diagnostics) {
641
+ if (!supportedDialects.has(catalog.dialect)) diagnostics.push(errorDiagnostic("unsupported-snapshot", `Code generation does not support catalog dialect "${catalog.dialect}"`, ["catalog", "dialect"]));
642
+ if (snapshot.dialect.name !== catalog.dialect) diagnostics.push(errorDiagnostic("unsupported-snapshot", `Snapshot dialect "${snapshot.dialect.name}" does not match catalog dialect "${catalog.dialect}"`, ["snapshot", "dialect"], void 0, [["catalog", "dialect"]]));
643
+ if (snapshot.dialect.version !== 1) diagnostics.push(errorDiagnostic("unsupported-snapshot", `Code generation supports Snapshot v1 dialect extensions, received version ${snapshot.dialect.version}`, [
644
+ "snapshot",
645
+ "dialect",
646
+ "version"
647
+ ]));
648
+ if (snapshot.namespace !== catalog.namespace.name) diagnostics.push(errorDiagnostic("unsupported-snapshot", "Snapshot and catalog namespaces do not match", ["snapshot", "namespace"], void 0, [[
649
+ "catalog",
650
+ "namespace",
651
+ "name"
652
+ ]]));
653
+ }
654
+ function validatePhysicalFacts(catalog, snapshot, diagnostics) {
655
+ const remapped = mapCatalogToSnapshot(catalog, {
656
+ namespace: catalog.namespace.name,
657
+ mode: "strict"
658
+ });
659
+ if (!remapped.ok) {
660
+ diagnostics.push(errorDiagnostic("omitted-fact", "The normalized catalog cannot be mapped to a complete strict Snapshot v1", ["catalog", "tables"], "Resolve the strict introspection diagnostics before generating source."));
661
+ return;
662
+ }
663
+ const expected = JSON.stringify(toPhysicalFacts(remapped.snapshot));
664
+ if (JSON.stringify(toPhysicalFacts(snapshot)) !== expected) diagnostics.push(errorDiagnostic("omitted-fact", "The introspection snapshot does not contain the catalog's complete Snapshot v1 table facts", ["snapshot", "tables"], "Use the snapshot returned with this exact strict catalog result."));
665
+ }
666
+ function validateSchemaProofs(snapshot, diagnostics) {
667
+ const tables = new Map(snapshot.tables.map((table) => [table.id, table]));
668
+ for (const table of snapshot.tables) {
669
+ const columns = new Map(table.columns.map((column) => [column.id, column]));
670
+ const path = [
671
+ "snapshot",
672
+ "tables",
673
+ table.id
674
+ ];
675
+ for (const constraint of table.constraints) {
676
+ if (constraint.kind !== "primary-key" && constraint.kind !== "unique") continue;
677
+ if (constraint.columns.filter((column) => columns.get(column)?.nullable === true).length > 0) diagnostics.push(errorDiagnostic("unrepresentable-fact", `Strict key constraint "${constraint.physicalName}" contains nullable columns`, [
678
+ ...path,
679
+ "constraints",
680
+ constraint.id,
681
+ "columns"
682
+ ], "Qubu primary and strict unique keys require every column to be non-nullable."));
683
+ }
684
+ for (const index of table.indexes) {
685
+ if (!index.candidateKey) continue;
686
+ const candidateColumns = candidateIndexColumns(index);
687
+ if (candidateColumns === void 0 || candidateColumns.some((column) => columns.get(column)?.nullable !== false)) diagnostics.push(errorDiagnostic("unrepresentable-fact", `Index "${index.physicalName}" is marked as a candidate key without an exact non-null column key`, [
688
+ ...path,
689
+ "indexes",
690
+ index.id,
691
+ "candidateKey"
692
+ ]));
693
+ }
694
+ for (const constraint of table.constraints) {
695
+ if (constraint.kind !== "foreign-key") continue;
696
+ const constraintPath = [
697
+ ...path,
698
+ "constraints",
699
+ constraint.id
700
+ ];
701
+ if (constraint.columns.length !== constraint.target.columns.length) {
702
+ diagnostics.push(errorDiagnostic("unrepresentable-fact", `Foreign key "${constraint.physicalName}" has unequal local and target arity`, [
703
+ ...constraintPath,
704
+ "target",
705
+ "columns"
706
+ ]));
707
+ continue;
708
+ }
709
+ const target = tables.get(constraint.target.table);
710
+ if (target === void 0) continue;
711
+ if (!hasCandidateKey(target, constraint.target.columns)) diagnostics.push(errorDiagnostic("unrepresentable-fact", `Foreign key "${constraint.physicalName}" does not target an exact primary key, strict unique key, or candidate index`, [
712
+ ...constraintPath,
713
+ "target",
714
+ "columns"
715
+ ], "Nullable UNIQUE constraints are database uniqueness metadata, not Qubu candidate-key proofs."));
716
+ }
717
+ }
718
+ }
719
+ function hasCandidateKey(table, targetColumns) {
720
+ const columns = new Map(table.columns.map((column) => [column.id, column]));
721
+ if (table.constraints.some((constraint) => (constraint.kind === "primary-key" || constraint.kind === "unique") && constraint.columns.every((column) => columns.get(column)?.nullable === false) && sameColumns(constraint.columns, targetColumns))) return true;
722
+ return table.indexes.some((index) => {
723
+ if (!index.candidateKey) return false;
724
+ const candidateColumns = candidateIndexColumns(index);
725
+ return candidateColumns !== void 0 && candidateColumns.every((column) => columns.get(column)?.nullable === false) && sameColumns(candidateColumns, targetColumns);
726
+ });
727
+ }
728
+ function candidateIndexColumns(index) {
729
+ if (!index.candidateKey || !index.unique || index.predicate !== void 0) return;
730
+ const columns = [];
731
+ for (const term of index.terms) {
732
+ const expression = term.kind === "order" ? term.expression : term;
733
+ if (expression.kind !== "column") return void 0;
734
+ columns.push(expression.column);
735
+ }
736
+ return columns.length > 0 ? columns : void 0;
737
+ }
738
+ function sameColumns(left, right) {
739
+ return left.length === right.length && left.every((column, index) => column === right[index]);
740
+ }
741
+ function resolveSchema(catalog, snapshot, options, diagnostics) {
742
+ const schemaSuggested = `${camelCase(catalog.namespace.name)}Schema`;
743
+ const schemaName = resolveName({
744
+ kind: "schema",
745
+ physicalName: catalog.namespace.name,
746
+ suggestedName: schemaSuggested
747
+ }, options, ["schema"], true, diagnostics);
748
+ const catalogTables = groupUnique(catalog.tables, (table) => table.physicalName, ["catalog", "tables"], diagnostics);
749
+ const tables = [];
750
+ const tableNames = /* @__PURE__ */ new Map();
751
+ for (const table of [...snapshot.tables].sort(comparePhysicalName)) {
752
+ const catalogTable = catalogTables.get(table.physicalName);
753
+ if (catalogTable === void 0) {
754
+ diagnostics.push(errorDiagnostic("omitted-fact", `Snapshot table "${table.physicalName}" is missing from the catalog`, [
755
+ "snapshot",
756
+ "tables",
757
+ table.id
758
+ ]));
759
+ continue;
760
+ }
761
+ const path = [
762
+ "snapshot",
763
+ "tables",
764
+ table.id
765
+ ];
766
+ const name = resolveName({
767
+ kind: "table",
768
+ physicalName: table.physicalName,
769
+ suggestedName: camelCase(table.physicalName)
770
+ }, options, path, true, diagnostics);
771
+ if (name === void 0) continue;
772
+ addUniqueName(name, path, tableNames, diagnostics);
773
+ const resolvedTable = resolveTable(table, catalogTable, name, catalog, options, diagnostics);
774
+ if (resolvedTable !== void 0) tables.push(resolvedTable);
775
+ }
776
+ if (schemaName !== void 0) {
777
+ const tableCollision = tableNames.get(schemaName);
778
+ if (tableCollision !== void 0) diagnostics.push(errorDiagnostic("name-collision", `Schema export and table export both resolve to "${schemaName}"`, ["schema"], void 0, [tableCollision]));
779
+ }
780
+ if (schemaName === void 0 || hasErrors(diagnostics)) return void 0;
781
+ const orderedTables = tables.sort(compareName);
782
+ return {
783
+ name: schemaName,
784
+ namespace: catalog.namespace.name,
785
+ dialect: catalog.dialect,
786
+ tables: orderedTables,
787
+ tablesBySnapshotId: new Map(orderedTables.map((table) => [table.snapshot.id, table]))
788
+ };
789
+ }
790
+ function resolveTable(snapshot, catalogTable, tableName, catalog, options, diagnostics) {
791
+ const columnCatalog = groupUnique(catalogTable.columns, (column) => column.physicalName, [
792
+ "catalog",
793
+ "tables",
794
+ catalogTable.id,
795
+ "columns"
796
+ ], diagnostics);
797
+ const columns = [];
798
+ const columnNames = /* @__PURE__ */ new Map();
799
+ for (const column of [...snapshot.columns].sort(comparePhysicalName)) {
800
+ const path = [
801
+ "snapshot",
802
+ "tables",
803
+ snapshot.id,
804
+ "columns",
805
+ column.id
806
+ ];
807
+ const catalogColumn = columnCatalog.get(column.physicalName);
808
+ if (catalogColumn === void 0) {
809
+ diagnostics.push(errorDiagnostic("omitted-fact", `Snapshot column "${column.physicalName}" is missing from its catalog table`, path));
810
+ continue;
811
+ }
812
+ if (column.storage?.kind !== "native") {
813
+ diagnostics.push(errorDiagnostic("unrepresentable-fact", `Introspected column "${column.physicalName}" must retain exact native storage`, [...path, "storage"]));
814
+ continue;
815
+ }
816
+ if (column.storage.dialect !== catalog.dialect) {
817
+ diagnostics.push(errorDiagnostic("unrepresentable-fact", `Column storage dialect "${column.storage.dialect}" does not match "${catalog.dialect}"`, [
818
+ ...path,
819
+ "storage",
820
+ "dialect"
821
+ ]));
822
+ continue;
823
+ }
824
+ if (column.storage.type.trim().length === 0) {
825
+ diagnostics.push(errorDiagnostic("unrepresentable-fact", "Native storage declarations cannot be empty", [
826
+ ...path,
827
+ "storage",
828
+ "type"
829
+ ]));
830
+ continue;
831
+ }
832
+ if (!hasCompleteColumnBehavior(column)) {
833
+ diagnostics.push(errorDiagnostic("omitted-fact", `Column "${column.physicalName}" omits metadata required to reconstruct its write behavior`, path));
834
+ continue;
835
+ }
836
+ const name = resolveName({
837
+ kind: "column",
838
+ physicalName: column.physicalName,
839
+ suggestedName: camelCase(column.physicalName),
840
+ tablePhysicalName: snapshot.physicalName,
841
+ tableName
842
+ }, options, path, false, diagnostics);
843
+ if (name === void 0) continue;
844
+ addUniqueName(name, path, columnNames, diagnostics);
845
+ const suggestedSqlDomain = inferSqlDomain(catalog.dialect, catalogColumn);
846
+ const mapping = resolveColumnMapping(catalog, catalogTable.physicalName, tableName, catalogColumn, name, suggestedSqlDomain, options, path, diagnostics);
847
+ if (mapping === void 0) continue;
848
+ columns.push({
849
+ name,
850
+ snapshot: column,
851
+ catalog: catalogColumn,
852
+ output: mapping.output ?? "unknown",
853
+ insert: mapping.insert ?? "unknown",
854
+ update: mapping.update ?? "unknown",
855
+ sqlDomain: mapping.sqlDomain ?? suggestedSqlDomain,
856
+ explicitSqlDomain: mapping.sqlDomain !== void 0
857
+ });
858
+ }
859
+ const constraints = resolveObjects(snapshot.constraints, "constraint", snapshot, tableName, options, diagnostics);
860
+ const indexes = resolveObjects(snapshot.indexes, "index", snapshot, tableName, options, diagnostics);
861
+ if (hasErrors(diagnostics)) return void 0;
862
+ const orderedColumns = columns.sort(compareName);
863
+ return {
864
+ name: tableName,
865
+ snapshot,
866
+ catalog: catalogTable,
867
+ columns: orderedColumns,
868
+ columnsBySnapshotId: new Map(orderedColumns.map((column) => [column.snapshot.id, column])),
869
+ constraints,
870
+ indexes
871
+ };
872
+ }
873
+ function resolveObjects(objects, kind, table, tableName, options, diagnostics) {
874
+ const names = /* @__PURE__ */ new Map();
875
+ const result = [];
876
+ for (const object of [...objects].sort(comparePhysicalName)) {
877
+ const path = [
878
+ "snapshot",
879
+ "tables",
880
+ table.id,
881
+ kind === "constraint" ? "constraints" : "indexes",
882
+ object.id
883
+ ];
884
+ const name = resolveName({
885
+ kind,
886
+ physicalName: object.physicalName,
887
+ suggestedName: camelCase(object.physicalName),
888
+ tablePhysicalName: table.physicalName,
889
+ tableName
890
+ }, options, path, false, diagnostics);
891
+ if (name === void 0) continue;
892
+ addUniqueName(name, path, names, diagnostics);
893
+ result.push({
894
+ name,
895
+ snapshot: object
896
+ });
897
+ }
898
+ return result.sort(compareName);
899
+ }
900
+ function resolveColumnMapping(catalog, tablePhysicalName, tableName, column, columnName, suggestedSqlDomain, options, path, diagnostics) {
901
+ if (options.mapColumn === void 0) return {};
902
+ if (typeof options.mapColumn !== "function") {
903
+ diagnostics.push(errorDiagnostic("invalid-option", "mapColumn must be a function", ["options", "mapColumn"]));
904
+ return;
905
+ }
906
+ let mapping;
907
+ try {
908
+ mapping = options.mapColumn({
909
+ dialect: catalog.dialect,
910
+ namespace: catalog.namespace.name,
911
+ tablePhysicalName,
912
+ tableName,
913
+ columnPhysicalName: column.physicalName,
914
+ columnName,
915
+ nativeType: column.storage.nativeType,
916
+ portableType: column.storage.portable?.type,
917
+ classificationConfidence: column.storage.portable?.confidence,
918
+ suggestedSqlDomain
919
+ });
920
+ } catch (caught) {
921
+ diagnostics.push(errorDiagnostic("invalid-option", caught instanceof Error ? `Column mapping failed: ${caught.message}` : "Column mapping failed", [...path, "mapping"]));
922
+ return;
923
+ }
924
+ if (mapping === void 0) return {};
925
+ if (typeof mapping !== "object" || mapping === null || Array.isArray(mapping)) {
926
+ diagnostics.push(errorDiagnostic("invalid-option", "Column mappings must return an object or undefined", [...path, "mapping"]));
927
+ return;
928
+ }
929
+ for (const axis of [
930
+ "output",
931
+ "insert",
932
+ "update"
933
+ ]) {
934
+ const value = mapping[axis];
935
+ if (value !== void 0 && !applicationTypes.has(value)) diagnostics.push(errorDiagnostic("invalid-option", `Unsupported ${axis} application type "${String(value)}"`, [
936
+ ...path,
937
+ "mapping",
938
+ axis
939
+ ]));
940
+ }
941
+ if (mapping.sqlDomain !== void 0 && !sqlDomains.has(mapping.sqlDomain)) diagnostics.push(errorDiagnostic("invalid-option", `Unsupported SQL domain "${String(mapping.sqlDomain)}"`, [
942
+ ...path,
943
+ "mapping",
944
+ "sqlDomain"
945
+ ]));
946
+ return hasErrors(diagnostics) ? void 0 : mapping;
947
+ }
948
+ function validateResolvedDomains(schema, diagnostics) {
949
+ for (const table of schema.tables) for (const index of table.indexes) for (const term of index.snapshot.terms) {
950
+ const ordered = term.kind === "order" ? term : void 0;
951
+ if (ordered?.expression.kind !== "column") continue;
952
+ const column = table.columnsBySnapshotId.get(ordered.expression.column);
953
+ if (column === void 0 || orderableDomains.has(column.sqlDomain)) continue;
954
+ if (column.explicitSqlDomain) diagnostics.push(errorDiagnostic("invalid-option", `SQL domain "${column.sqlDomain}" cannot represent an ordered index term`, [
955
+ "snapshot",
956
+ "tables",
957
+ table.snapshot.id,
958
+ "indexes",
959
+ index.snapshot.id,
960
+ "terms"
961
+ ]));
962
+ else column.sqlDomain = "unknown";
963
+ }
964
+ for (const table of schema.tables) for (const constraint of table.constraints) {
965
+ if (constraint.snapshot.kind !== "foreign-key") continue;
966
+ const targetTable = schema.tablesBySnapshotId.get(constraint.snapshot.target.table);
967
+ if (targetTable === void 0) continue;
968
+ for (const [index, localId] of constraint.snapshot.columns.entries()) {
969
+ const targetId = constraint.snapshot.target.columns[index];
970
+ const local = table.columnsBySnapshotId.get(localId);
971
+ const target = targetId ? targetTable.columnsBySnapshotId.get(targetId) : void 0;
972
+ if (local === void 0 || target === void 0 || local.sqlDomain === "unknown" || target.sqlDomain === "unknown" || local.sqlDomain === target.sqlDomain) continue;
973
+ if (local.explicitSqlDomain && target.explicitSqlDomain) diagnostics.push(errorDiagnostic("invalid-option", `Mapped foreign-key domains differ: "${local.sqlDomain}" and "${target.sqlDomain}"`, [
974
+ "snapshot",
975
+ "tables",
976
+ table.snapshot.id,
977
+ "constraints",
978
+ constraint.snapshot.id,
979
+ "columns",
980
+ index
981
+ ]));
982
+ else if (!local.explicitSqlDomain) local.sqlDomain = "unknown";
983
+ else target.sqlDomain = "unknown";
984
+ }
985
+ }
986
+ }
987
+ function inferSqlDomain(dialect, column) {
988
+ const portable = column.storage.portable;
989
+ if (portable?.confidence === "exact") return portableDomain(portable.type);
990
+ const declaration = column.storage.nativeType.trim().toLowerCase();
991
+ if (dialect === "postgresql") switch (declaration) {
992
+ case "smallint":
993
+ case "integer": return "integer";
994
+ case "bigint": return "bigint";
995
+ case "numeric": return "decimal";
996
+ case "text": return "text";
997
+ case "boolean": return "boolean";
998
+ case "date": return "date";
999
+ case "timestamp without time zone":
1000
+ case "timestamp with time zone": return "timestamp";
1001
+ case "uuid": return "uuid";
1002
+ case "json":
1003
+ case "jsonb": return "json";
1004
+ case "bytea": return "binary";
1005
+ default: return "unknown";
1006
+ }
1007
+ if (dialect === "mysql") switch (declaration) {
1008
+ case "tinyint":
1009
+ case "smallint":
1010
+ case "mediumint":
1011
+ case "int": return "integer";
1012
+ case "bigint": return "bigint";
1013
+ case "decimal": return "decimal";
1014
+ case "text":
1015
+ case "tinytext":
1016
+ case "mediumtext":
1017
+ case "longtext": return "text";
1018
+ case "date": return "date";
1019
+ case "datetime":
1020
+ case "timestamp": return "timestamp";
1021
+ case "json": return "json";
1022
+ case "blob":
1023
+ case "tinyblob":
1024
+ case "mediumblob":
1025
+ case "longblob": return "binary";
1026
+ default: return "unknown";
1027
+ }
1028
+ return "unknown";
1029
+ }
1030
+ function portableDomain(portable) {
1031
+ return portable === "numeric" ? "decimal" : portable;
1032
+ }
1033
+ function resolveName(context, options, path, exportBinding, diagnostics) {
1034
+ let name = context.suggestedName;
1035
+ if (options.naming !== void 0) {
1036
+ if (typeof options.naming !== "function") {
1037
+ diagnostics.push(errorDiagnostic("invalid-option", "naming must be a function", ["options", "naming"]));
1038
+ return;
1039
+ }
1040
+ try {
1041
+ name = options.naming(context) ?? context.suggestedName;
1042
+ } catch (caught) {
1043
+ diagnostics.push(errorDiagnostic("invalid-option", caught instanceof Error ? `Naming callback failed: ${caught.message}` : "Naming callback failed", [...path, "name"]));
1044
+ return;
1045
+ }
1046
+ }
1047
+ if (typeof name !== "string" || !/^[a-z][A-Za-z0-9]*$/u.test(name) || exportBinding && reservedBindings.has(name)) {
1048
+ diagnostics.push(errorDiagnostic("unsafe-name", `Physical ${context.kind} name ${JSON.stringify(context.physicalName)} does not resolve to a safe camelCase${exportBinding ? " export" : ""} identifier`, [...path, "name"], "Return a safe camelCase ID from the naming callback."));
1049
+ return;
1050
+ }
1051
+ return name;
1052
+ }
1053
+ function camelCase(value) {
1054
+ const words = value.replace(/([a-z0-9])([A-Z])/gu, "$1 $2").split(/[^A-Za-z0-9]+/u).filter(Boolean).map((word) => word.toLowerCase());
1055
+ return `${words.shift() ?? ""}${words.map((word) => `${word.slice(0, 1).toUpperCase()}${word.slice(1)}`).join("")}`;
1056
+ }
1057
+ function hasCompleteColumnBehavior(column) {
1058
+ if ([
1059
+ column.default,
1060
+ column.generatedColumn,
1061
+ column.identity
1062
+ ].filter((value) => value !== void 0).length > 1) return false;
1063
+ if (column.generatedColumn !== void 0 || column.identity !== void 0) return column.generated && !column.hasDefault;
1064
+ if (column.default !== void 0) return !column.generated && column.hasDefault;
1065
+ return !column.generated && !column.hasDefault;
1066
+ }
1067
+ function toPhysicalFacts(snapshot) {
1068
+ const tableById = new Map(snapshot.tables.map((table) => [table.id, table]));
1069
+ const columnNames = new Map(snapshot.tables.map((table) => [table.id, new Map(table.columns.map((column) => [column.id, column.physicalName]))]));
1070
+ return {
1071
+ dialect: snapshot.dialect,
1072
+ namespace: snapshot.namespace ?? null,
1073
+ tables: [...snapshot.tables].sort(comparePhysicalName).map((table) => ({
1074
+ physicalName: table.physicalName,
1075
+ columns: [...table.columns].sort(comparePhysicalName).map((column) => ({
1076
+ ...column,
1077
+ id: column.physicalName
1078
+ })),
1079
+ constraints: [...table.constraints].sort(comparePhysicalName).map((constraint) => physicalConstraint(constraint, table, tableById, columnNames)),
1080
+ indexes: [...table.indexes].sort(comparePhysicalName).map((index) => physicalIndex(index, table, columnNames))
1081
+ }))
1082
+ };
1083
+ }
1084
+ function physicalConstraint(constraint, table, tables, columns) {
1085
+ if (constraint.kind === "check") return {
1086
+ ...constraint,
1087
+ id: constraint.physicalName
1088
+ };
1089
+ const localColumns = constraint.columns.map((id) => columns.get(table.id)?.get(id) ?? `missing:${id}`);
1090
+ if (constraint.kind !== "foreign-key") return {
1091
+ ...constraint,
1092
+ id: constraint.physicalName,
1093
+ columns: localColumns
1094
+ };
1095
+ const targetTable = tables.get(constraint.target.table);
1096
+ return {
1097
+ ...constraint,
1098
+ id: constraint.physicalName,
1099
+ columns: localColumns,
1100
+ target: {
1101
+ table: targetTable?.physicalName ?? `missing:${constraint.target.table}`,
1102
+ columns: constraint.target.columns.map((id) => columns.get(constraint.target.table)?.get(id) ?? `missing:${id}`)
1103
+ }
1104
+ };
1105
+ }
1106
+ function physicalIndex(index, table, columns) {
1107
+ const names = columns.get(table.id);
1108
+ return {
1109
+ ...index,
1110
+ id: index.physicalName,
1111
+ terms: index.terms.map((term) => physicalIndexTerm(term, names)),
1112
+ ...index.includedColumns === void 0 ? {} : { includedColumns: index.includedColumns.map((id) => names?.get(id) ?? `missing:${id}`) }
1113
+ };
1114
+ }
1115
+ function physicalIndexTerm(term, columns) {
1116
+ if (term.kind === "column") return {
1117
+ ...term,
1118
+ column: columns?.get(term.column) ?? `missing:${term.column}`
1119
+ };
1120
+ if (term.kind === "expression") return term;
1121
+ return {
1122
+ ...term,
1123
+ expression: term.expression.kind === "column" ? {
1124
+ ...term.expression,
1125
+ column: columns?.get(term.expression.column) ?? `missing:${term.expression.column}`
1126
+ } : term.expression
1127
+ };
1128
+ }
1129
+ function appendExcludedFamilyDiagnostics(catalog, diagnostics) {
1130
+ if (catalog === void 0) return;
1131
+ for (const [property, label] of excludedFamilies) {
1132
+ const values = catalog[property];
1133
+ if (values === void 0 || values.length === 0) continue;
1134
+ diagnostics.push({
1135
+ severity: "warning",
1136
+ code: "excluded-object-family",
1137
+ message: `Snapshot v1 source generation excludes ${values.length} ${label}`,
1138
+ path: Object.freeze(["catalog", property]),
1139
+ remediation: "Use the complete normalized catalog or Snapshot v2 for this object family."
1140
+ });
1141
+ }
1142
+ }
1143
+ function groupUnique(values, keyOf, path, diagnostics) {
1144
+ const result = /* @__PURE__ */ new Map();
1145
+ for (const [index, value] of values.entries()) {
1146
+ const key = keyOf(value);
1147
+ if (result.has(key)) diagnostics.push(errorDiagnostic("name-collision", `Physical name ${JSON.stringify(key)} occurs more than once`, [...path, index]));
1148
+ else result.set(key, value);
1149
+ }
1150
+ return result;
1151
+ }
1152
+ function addUniqueName(name, path, names, diagnostics) {
1153
+ const existing = names.get(name);
1154
+ if (existing === void 0) {
1155
+ names.set(name, path);
1156
+ return;
1157
+ }
1158
+ diagnostics.push(errorDiagnostic("name-collision", `More than one physical object resolves to logical ID "${name}"`, [...path, "name"], "Override one logical ID with the naming callback.", [existing]));
1159
+ }
1160
+ function errorDiagnostic(code, message, path, remediation, relatedPaths) {
1161
+ return {
1162
+ severity: "error",
1163
+ code,
1164
+ message,
1165
+ path,
1166
+ ...remediation === void 0 ? {} : { remediation },
1167
+ ...relatedPaths === void 0 ? {} : { relatedPaths }
1168
+ };
1169
+ }
1170
+ function failure(diagnostics) {
1171
+ return Object.freeze({
1172
+ ok: false,
1173
+ diagnostics: freezeDiagnostics(diagnostics)
1174
+ });
1175
+ }
1176
+ function freezeDiagnostics(diagnostics) {
1177
+ return Object.freeze(diagnostics.map((diagnostic) => Object.freeze({
1178
+ ...diagnostic,
1179
+ path: Object.freeze([...diagnostic.path]),
1180
+ relatedPaths: diagnostic.relatedPaths === void 0 ? void 0 : Object.freeze(diagnostic.relatedPaths.map((path) => Object.freeze([...path])))
1181
+ })));
1182
+ }
1183
+ function hasErrors(diagnostics) {
1184
+ return diagnostics.some((diagnostic) => diagnostic.severity === "error");
1185
+ }
1186
+ function comparePhysicalName(left, right) {
1187
+ return left.physicalName < right.physicalName ? -1 : left.physicalName > right.physicalName ? 1 : 0;
1188
+ }
1189
+ function compareName(left, right) {
1190
+ return left.name < right.name ? -1 : left.name > right.name ? 1 : 0;
1191
+ }
1192
+ function isOptions(value) {
1193
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1194
+ }
1195
+ function isIntrospectionResult(value) {
1196
+ return typeof value === "object" && value !== null && "ok" in value && typeof value.ok === "boolean" && "lossy" in value && typeof value.lossy === "boolean" && "diagnostics" in value && Array.isArray(value.diagnostics);
1197
+ }
1198
+ //#endregion
1199
+ export { generateSchemaSource };