qubu 0.6.0 → 0.6.2

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 (95) hide show
  1. package/README.md +112 -0
  2. package/dist/{complete-DP7pliuY.mjs → canonical-B5_ouh-c.mjs} +466 -241
  3. package/dist/codegen.d.mts +2 -2
  4. package/dist/codegen.mjs +180 -90
  5. package/dist/column-Cyc2CMnG.mjs +116 -0
  6. package/dist/column-DDRvD7SF.mjs +721 -0
  7. package/dist/{constraints-YGyNPQ_z.mjs → constraints-CAmi18Uk.mjs} +8 -3
  8. package/dist/core.d.mts +3 -3
  9. package/dist/core.mjs +4 -5
  10. package/dist/diagnostics-I9vVtXkc.mjs +40 -0
  11. package/dist/diff.d.mts +9 -9
  12. package/dist/diff.mjs +152 -102
  13. package/dist/{explain-CkIK13L_.mjs → explain-BIqEmZha.mjs} +1 -1
  14. package/dist/expressions-_6JF_J77.mjs +130 -0
  15. package/dist/index-CaxrMD1A.d.mts +1 -0
  16. package/dist/index.d.mts +2 -2
  17. package/dist/index.mjs +414 -77
  18. package/dist/introspection/mysql.d.mts +26 -0
  19. package/dist/introspection/mysql.mjs +1278 -0
  20. package/dist/introspection/postgres.d.mts +43 -0
  21. package/dist/introspection/postgres.mjs +1889 -0
  22. package/dist/introspection/sqlite.d.mts +15 -0
  23. package/dist/introspection/sqlite.mjs +1172 -0
  24. package/dist/introspection.d.mts +26 -87
  25. package/dist/introspection.mjs +3 -4351
  26. package/dist/mysql.d.mts +3 -3
  27. package/dist/mysql.mjs +8 -7
  28. package/dist/{errors-Dxv73YJu.mjs → omit-VEr9Ydux.mjs} +5 -1
  29. package/dist/{on-conflict-DZQ85f1t.mjs → on-conflict-B2rFyHGF.mjs} +78 -8
  30. package/dist/on-duplicate-key-update-Czsw1Yq-.mjs +95 -0
  31. package/dist/postgres-hFhd0I9n.mjs +68 -0
  32. package/dist/postgres.d.mts +3 -3
  33. package/dist/postgres.mjs +3 -52
  34. package/dist/registry-BXE_4M9P.mjs +127 -0
  35. package/dist/{relational-CxnLCqZQ.mjs → relational-CoPBETjI.mjs} +3 -3
  36. package/dist/schema.d.mts +2 -2
  37. package/dist/schema.mjs +8 -7
  38. package/dist/{serialize-BN07IK0v.mjs → serialize-CyobNEx-.mjs} +174 -30
  39. package/dist/serialize-Du2UPZMt.d.mts +92 -0
  40. package/dist/snapshot/mysql.d.mts +5 -5
  41. package/dist/snapshot/mysql.mjs +32 -32
  42. package/dist/snapshot/postgres.d.mts +5 -5
  43. package/dist/snapshot/postgres.mjs +25 -25
  44. package/dist/snapshot/sqlite.d.mts +5 -5
  45. package/dist/snapshot/sqlite.mjs +30 -30
  46. package/dist/snapshot-mIb-Zzb5.mjs +1489 -0
  47. package/dist/snapshot.d.mts +4 -5
  48. package/dist/snapshot.mjs +3 -4
  49. package/dist/{source-SqrKWjFJ.mjs → source-DYSUqzvb.mjs} +2 -2
  50. package/dist/sqlite.d.mts +2 -2
  51. package/dist/sqlite.mjs +10 -10
  52. package/dist/{table-BwflqeAj.mjs → table-B8zEq0az.mjs} +4 -4
  53. package/dist/{types-BIJsj2fJ.mjs → types-CYHpSPwj.mjs} +10 -7
  54. package/dist/{types-CTCqtFlS.d.mts → types-CiMvKi5V.d.mts} +14 -4
  55. package/dist/{types-C0VkiwpR.d.mts → types-Dqr4o2I1.d.mts} +640 -189
  56. package/dist/value-CpaUFtjw.mjs +45 -0
  57. package/dist/vite/ambient.d.ts +2 -0
  58. package/dist/vite.d.mts +1 -1
  59. package/dist/vite.mjs +2 -0
  60. package/docs/dialects-and-execution.md +61 -11
  61. package/docs/getting-started.md +1 -1
  62. package/docs/guides/compose-queries.md +22 -0
  63. package/docs/guides/drizzle.md +11 -11
  64. package/docs/guides/extensions/typed-expressions.md +5 -3
  65. package/docs/guides/json.md +95 -2
  66. package/docs/guides/mutations.md +125 -0
  67. package/docs/guides/valtio-sync.md +113 -0
  68. package/docs/index.md +1 -1
  69. package/docs/migrations/adapters.md +58 -21
  70. package/docs/migrations/index.md +50 -12
  71. package/docs/migrations/operations.md +20 -6
  72. package/docs/migrations/recovery.md +7 -1
  73. package/docs/query-model/fragments.md +21 -1
  74. package/docs/reference/introspection-support.md +16 -18
  75. package/docs/reference/supported-surface.md +23 -11
  76. package/docs/schema/catalog-model.md +20 -7
  77. package/docs/schema/code-generation.md +4 -3
  78. package/docs/schema/diff.md +2 -2
  79. package/docs/schema/introspection.md +24 -16
  80. package/docs/schema/snapshots.md +18 -8
  81. package/docs/schema/storage-and-schema-sql.md +7 -2
  82. package/docs/sql-semantic-types.md +8 -0
  83. package/package.json +14 -1
  84. package/dist/canonical-DMvR9yBe.mjs +0 -972
  85. package/dist/column-Da37jYSD.mjs +0 -309
  86. package/dist/column-r1Y4ivwt.mjs +0 -327
  87. package/dist/complete-types-CY0KbzNw.d.mts +0 -371
  88. package/dist/index-B2rZf3-2.d.mts +0 -32
  89. package/dist/json-Db7XRD91.mjs +0 -169
  90. package/dist/omit-OxV58AwX.mjs +0 -5
  91. package/dist/registry-BRcUuazJ.mjs +0 -256
  92. package/dist/serialize-CEIIlWhC.d.mts +0 -66
  93. package/dist/snapshot-Xam8-q0j.mjs +0 -392
  94. package/dist/standard-DfcZEVOj.mjs +0 -12
  95. package/dist/value-D14I_XgL.mjs +0 -29
@@ -1,4 +1,4 @@
1
- import { St as IntrospectionDiagnostic, dt as IntrospectionResult, i as CatalogClassificationConfidence, p as CatalogDialect, z as CatalogPortableStorageType } from "./types-CTCqtFlS.mjs";
1
+ import { V as CatalogPortableStorageType, i as CatalogClassificationConfidence, p as CatalogDialect, pt as IntrospectionResult, wt as IntrospectionDiagnostic } from "./types-CiMvKi5V.mjs";
2
2
  //#region src/codegen/types.d.ts
3
3
  /** TypeScript value types that the controlled source printer can emit. */
4
4
  type CodegenApplicationType = "unknown" | "string" | "number" | "boolean" | "bigint" | "Date" | "Uint8Array";
@@ -84,7 +84,7 @@ type SchemaCodegenResult = SchemaCodegenSuccess | SchemaCodegenFailure;
84
84
  * expressions use dialect-tagged schema data, checks use `catalogCheck()`, and foreign keys use
85
85
  * lazy `catalogForeignKey()` targets. Foreign keys must have equal local and target arity and
86
86
  * target an exact primary key, strict unique key, or candidate index. Nullable database UNIQUE
87
- * constraints do not provide that candidate-key proof and fail generation. Application output,
87
+ * constraints and indexes with lossy candidate-key facts do not provide that proof. Application output,
88
88
  * insert, and update types default independently to `unknown`. The generator may attach a
89
89
  * conservative SQL semantic domain, and {@link SchemaCodegenOptions.mapColumn} can override either
90
90
  * type axis with a fixed token. Naming callbacks can replace suggested camelCase IDs, but they
package/dist/codegen.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { l as decodeSchemaSnapshot } from "./canonical-DMvR9yBe.mjs";
2
- import { t as mapCatalogToSnapshot } from "./snapshot-Xam8-q0j.mjs";
1
+ import { c as candidateKeyIndexColumns, l as hasCandidateKeyShape, t as mapCatalogToSnapshot, u as isCandidateKeyIndex } from "./snapshot-mIb-Zzb5.mjs";
2
+ import { l as decodeSchemaSnapshot } from "./canonical-B5_ouh-c.mjs";
3
3
  //#region src/codegen/source.ts
4
4
  const header = `/* Generated by Qubu. Do not edit this machine-owned file. */
5
5
  import * as _qubu from 'qubu'
@@ -62,7 +62,22 @@ function printTable(schema, table, diagnostics) {
62
62
  const tableTypeName = `${typePrefix}Table`;
63
63
  const constraintTypes = table.constraints.map((constraint) => printConstraintType(table, constraint, columnsName, diagnostics));
64
64
  const indexTypes = table.indexes.map((index) => printIndexType(table, index, columnsName, diagnostics));
65
- if (constraintTypes.some((value) => value === void 0) || indexTypes.some((value) => value === void 0)) return;
65
+ const tableDialect = table.snapshot.dialect === void 0 ? void 0 : printExtension(table.snapshot.dialect, schema, [
66
+ "snapshot",
67
+ "tables",
68
+ table.snapshot.id,
69
+ "dialect"
70
+ ], diagnostics);
71
+ if (constraintTypes.some((value) => value === void 0) || indexTypes.some((value) => value === void 0) || table.snapshot.dialect !== void 0 && tableDialect === void 0) return;
72
+ const tableMetadata = [
73
+ `constraints: {
74
+ ${constraints.map((value) => indent(value ?? "", 6)).join("\n")}
75
+ }`,
76
+ `indexes: {
77
+ ${indexes.map((value) => indent(value ?? "", 6)).join("\n")}
78
+ }`,
79
+ ...tableDialect === void 0 ? [] : [`dialect: ${tableDialect}`]
80
+ ];
66
81
  return `const ${definitionsName} = {
67
82
  ${columns.map((value) => indent(value ?? "", 2)).join("\n")}
68
83
  }
@@ -93,12 +108,7 @@ export const ${table.name}: ${tableTypeName} = _qubu.table(
93
108
  ${literal(table.snapshot.physicalName)},
94
109
  ${definitionsName},
95
110
  _current => ({
96
- constraints: {
97
- ${constraints.map((value) => indent(value ?? "", 6)).join("\n")}
98
- },
99
- indexes: {
100
- ${indexes.map((value) => indent(value ?? "", 6)).join("\n")}
101
- },
111
+ ${tableMetadata.map((value) => indent(value, 4)).join(",\n")}
102
112
  })
103
113
  )`;
104
114
  }
@@ -117,7 +127,7 @@ function printConstraintType(table, constraint, columnsName, diagnostics) {
117
127
  if (snapshot.kind === "foreign-key") return `readonly ${literal(constraint.name)}: _qubu.ForeignKeyConstraint<${columns}>`;
118
128
  if (snapshot.kind === "unique-constraint") return `readonly ${literal(constraint.name)}: _qubu.UniqueConstraint<
119
129
  ${columns},
120
- ${literal(snapshot.nulls)}
130
+ ${literal(snapshot.nulls ?? "distinct")}
121
131
  >`;
122
132
  return `readonly ${literal(constraint.name)}: _qubu.KeyConstraint<
123
133
  ${literal(snapshot.kind)},
@@ -155,9 +165,9 @@ ${properties.map((property) => ` ${property}`).join("\n")}
155
165
  }`;
156
166
  }
157
167
  function printIndexTermType(table, term, columnsName, path, diagnostics) {
158
- if (term.kind !== "order") return printIndexTermExpressionType(table, term, columnsName, path, diagnostics);
159
- const expression = printIndexTermExpressionType(table, term.expression, columnsName, [...path, "expression"], diagnostics);
160
- return expression === void 0 ? void 0 : `_qubu.OrderTerm<unknown, ${expression}>`;
168
+ const expression = printIndexTermExpressionType(table, term, columnsName, path, diagnostics);
169
+ if (expression === void 0) return;
170
+ return term.direction === void 0 && term.nulls === void 0 ? expression : `_qubu.OrderTerm<unknown, ${expression}>`;
161
171
  }
162
172
  function printIndexTermExpressionType(table, expression, columnsName, path, diagnostics) {
163
173
  if (expression.kind === "expression") return "_qubu.AnyExpression";
@@ -198,6 +208,11 @@ function printColumn(schema, table, column, diagnostics) {
198
208
  return;
199
209
  }
200
210
  const options = [`nullable: ${snapshot.nullable ? "true" : "false"}`, `sqlName: ${literal(snapshot.physicalName)}`];
211
+ if (snapshot.dialect !== void 0) {
212
+ const extension = printExtension(snapshot.dialect, schema, [...path, "dialect"], diagnostics);
213
+ if (extension === void 0) return;
214
+ options.push(`dialect: ${extension}`);
215
+ }
201
216
  if (snapshot.default !== void 0) {
202
217
  if (snapshot.default.kind === "external") options.push("default: _qubu.externalDefault()");
203
218
  else if (snapshot.default.kind === "literal") {
@@ -233,7 +248,16 @@ function printColumn(schema, table, column, diagnostics) {
233
248
  "dialect"
234
249
  ], diagnostics) : void 0;
235
250
  if (snapshot.identity.dialect !== void 0 && extension === void 0) return;
236
- options.push(extension === void 0 ? `identity: _qubu.identityColumn(${literal(snapshot.identity.generation)})` : `identity: _qubu.identityColumn(${literal(snapshot.identity.generation)}, { dialect: ${extension} })`);
251
+ const identityProperties = [];
252
+ const identityOptions = printValueFactRecord(snapshot.identity.options, schema, [
253
+ ...path,
254
+ "identity",
255
+ "options"
256
+ ], diagnostics);
257
+ if (identityOptions === void 0) return;
258
+ if (identityOptions !== "{}") identityProperties.push(`options: ${identityOptions}`);
259
+ if (extension !== void 0) identityProperties.push(`dialect: ${extension}`);
260
+ options.push(identityProperties.length === 0 ? `identity: _qubu.identityColumn(${literal(snapshot.identity.generation)})` : `identity: _qubu.identityColumn(${literal(snapshot.identity.generation)}, { ${identityProperties.join(", ")} })`);
237
261
  }
238
262
  if (snapshot.onUpdate !== void 0) {
239
263
  const expression = printExpression(snapshot.onUpdate, schema, [...path, "onUpdate"], diagnostics);
@@ -264,7 +288,7 @@ function printConstraint(schema, table, constraint, diagnostics) {
264
288
  "constraints",
265
289
  snapshot.id
266
290
  ];
267
- const options = printConstraintOptions(snapshot, schema, path, diagnostics);
291
+ const options = printConstraintOptions(snapshot, schema, table, path, diagnostics);
268
292
  if (options === void 0) return;
269
293
  let expression;
270
294
  if (snapshot.kind === "check") {
@@ -273,9 +297,9 @@ function printConstraint(schema, table, constraint, diagnostics) {
273
297
  expression = `_qubu.catalogCheck(${sql}, ${options})`;
274
298
  } else if (snapshot.kind === "foreign-key") {
275
299
  const local = printColumns(table, snapshot.columns, path, diagnostics);
276
- const targetTable = schema.tablesBySnapshotId.get(snapshot.target.table);
300
+ const targetTable = schema.tablesBySnapshotId.get(snapshot.target.table.id);
277
301
  if (targetTable === void 0) {
278
- diagnostics.push(sourceError(`Foreign-key target table "${snapshot.target.table}" was not resolved`, [
302
+ diagnostics.push(sourceError(`Foreign-key target table "${snapshot.target.table.id}" was not resolved`, [
279
303
  ...path,
280
304
  "target",
281
305
  "table"
@@ -296,9 +320,9 @@ function printConstraint(schema, table, constraint, diagnostics) {
296
320
  }
297
321
  return `${literal(constraint.name)}: ${expression},`;
298
322
  }
299
- function printConstraintOptions(constraint, schema, path, diagnostics) {
323
+ function printConstraintOptions(constraint, schema, table, path, diagnostics) {
300
324
  const properties = [`physicalName: ${literal(constraint.physicalName)}`];
301
- if (constraint.kind === "unique-constraint") properties.push(`nulls: ${literal(constraint.nulls)}`);
325
+ if (constraint.kind === "unique-constraint") properties.push(`nulls: ${literal(constraint.nulls ?? "distinct")}`);
302
326
  if (constraint.kind === "foreign-key") {
303
327
  if (constraint.onUpdate !== void 0) properties.push(`onUpdate: ${literal(constraint.onUpdate)}`);
304
328
  if (constraint.onDelete !== void 0) properties.push(`onDelete: ${literal(constraint.onDelete)}`);
@@ -306,6 +330,15 @@ function printConstraintOptions(constraint, schema, path, diagnostics) {
306
330
  }
307
331
  if (constraint.deferrable !== void 0) properties.push(`deferrable: ${constraint.deferrable ? "true" : "false"}`);
308
332
  if (constraint.initially !== void 0) properties.push(`initially: ${literal(constraint.initially)}`);
333
+ if (constraint.validated !== void 0) properties.push(`validated: ${constraint.validated ? "true" : "false"}`);
334
+ if ((constraint.kind === "primary-key" || constraint.kind === "unique" || constraint.kind === "unique-constraint") && constraint.backingIndex !== void 0) {
335
+ const backingIndex = table.indexes.find((index) => index.snapshot.id === constraint.backingIndex?.id);
336
+ if (backingIndex === void 0) {
337
+ diagnostics.push(sourceError(`Backing index "${constraint.backingIndex.id}" was not resolved`, [...path, "backingIndex"]));
338
+ return;
339
+ }
340
+ properties.push(`backingIndex: ${literal(backingIndex.name)}`);
341
+ }
309
342
  if (constraint.dialect !== void 0) {
310
343
  const extension = printExtension(constraint.dialect, schema, [...path, "dialect"], diagnostics);
311
344
  if (extension === void 0) return;
@@ -328,6 +361,43 @@ function printIndex(schema, table, index, diagnostics) {
328
361
  ], diagnostics));
329
362
  if (terms.some((term) => term === void 0)) return;
330
363
  const options = [`physicalName: ${literal(index.snapshot.physicalName)}`, `unique: ${index.snapshot.unique ? "true" : "false"}`];
364
+ if (index.snapshot.method !== void 0) options.push(`method: ${literal(index.snapshot.method)}`);
365
+ if (index.snapshot.backingConstraint !== void 0) {
366
+ const backingConstraint = table.constraints.find((constraint) => constraint.snapshot.id === index.snapshot.backingConstraint?.id);
367
+ if (backingConstraint === void 0) {
368
+ diagnostics.push(sourceError(`Backing constraint "${index.snapshot.backingConstraint.id}" was not resolved`, [...path, "backingConstraint"]));
369
+ return;
370
+ }
371
+ options.push(`backingConstraint: ${literal(backingConstraint.name)}`);
372
+ }
373
+ const termOptions = index.snapshot.terms.map((term, termIndex) => {
374
+ const termPrefixLength = term.kind === "column" ? term.prefixLength : void 0;
375
+ if (termPrefixLength === void 0 && term.operatorClass === void 0) return "undefined";
376
+ const properties = [];
377
+ if (termPrefixLength !== void 0) {
378
+ if (term.kind !== "column") {
379
+ diagnostics.push(sourceError("Index prefix lengths require column terms", [
380
+ ...path,
381
+ "terms",
382
+ termIndex,
383
+ "prefixLength"
384
+ ]));
385
+ return;
386
+ }
387
+ const prefixLength = printValueFact(termPrefixLength, schema, [
388
+ ...path,
389
+ "terms",
390
+ termIndex,
391
+ "prefixLength"
392
+ ], diagnostics);
393
+ if (prefixLength === void 0) return;
394
+ properties.push(`prefixLength: ${prefixLength}`);
395
+ }
396
+ if (term.operatorClass !== void 0) properties.push(`operatorClass: ${literal(term.operatorClass)}`);
397
+ return `{ ${properties.join(", ")} }`;
398
+ });
399
+ if (termOptions.some((value) => value === void 0)) return;
400
+ if (termOptions.some((value) => value !== "undefined")) options.push(`termOptions: [${termOptions.join(", ")}]`);
331
401
  if (index.snapshot.predicate !== void 0) {
332
402
  const predicate = catalogExpressionData(index.snapshot.predicate, schema, [...path, "predicate"], diagnostics);
333
403
  if (predicate === void 0) return;
@@ -349,10 +419,9 @@ function printIndex(schema, table, index, diagnostics) {
349
419
  ),`;
350
420
  }
351
421
  function printIndexTerm(schema, table, term, path, diagnostics) {
352
- if (term.kind !== "order") return printIndexTermExpression(schema, table, term, path, diagnostics);
353
- const expression = printIndexTermExpression(schema, table, term.expression, [...path, "expression"], diagnostics);
422
+ const expression = printIndexTermExpression(schema, table, term, path, diagnostics);
354
423
  if (expression === void 0) return;
355
- return `_qubu.order(${expression}, ${term.direction === void 0 ? "undefined" : literal(term.direction)}, ${term.nulls === void 0 ? "undefined" : literal(term.nulls)})`;
424
+ return term.direction === void 0 && term.nulls === void 0 ? expression : `_qubu.order(${expression}, ${term.direction === void 0 ? "undefined" : literal(term.direction)}, ${term.nulls === void 0 ? "undefined" : literal(term.nulls)})`;
356
425
  }
357
426
  function printIndexTermExpression(schema, table, expression, path, diagnostics) {
358
427
  if (expression.kind === "expression") return printExpression(expression.expression, schema, [...path, "expression"], diagnostics);
@@ -399,6 +468,23 @@ function printExtension(extension, schema, path, diagnostics) {
399
468
  const entries = Object.keys(data).sort().map((key) => `[${literal(key)}]: ${printJsonValue(data[key])}`);
400
469
  return `{ dialect: ${literal(extension.dialect)}${entries.length === 0 ? "" : `, ${entries.join(", ")}`} }`;
401
470
  }
471
+ function printValueFactRecord(record, schema, path, diagnostics) {
472
+ const entries = [];
473
+ for (const key of Object.keys(record).sort()) {
474
+ const value = record[key];
475
+ const printed = value.kind === "literal" ? printSnapshotLiteral(value.value, [...path, key], diagnostics) : printExpression(value.expression, schema, [
476
+ ...path,
477
+ key,
478
+ "expression"
479
+ ], diagnostics);
480
+ if (printed === void 0) return;
481
+ entries.push(`[${literal(key)}]: ${printed}`);
482
+ }
483
+ return entries.length === 0 ? "{}" : `{ ${entries.join(", ")} }`;
484
+ }
485
+ function printValueFact(value, schema, path, diagnostics) {
486
+ return value.kind === "literal" ? printSnapshotLiteral(value.value, path, diagnostics) : printExpression(value.expression, schema, [...path, "expression"], diagnostics);
487
+ }
402
488
  function printSnapshotLiteral(value, path, diagnostics) {
403
489
  switch (value.kind) {
404
490
  case "null": return "null";
@@ -408,7 +494,7 @@ function printSnapshotLiteral(value, path, diagnostics) {
408
494
  case "number": {
409
495
  const number = Number(value.value);
410
496
  if (!Number.isFinite(number)) {
411
- diagnostics.push(sourceError("Default number literals must be finite", [...path, "default"]));
497
+ diagnostics.push(sourceError("Number literals must be finite", path));
412
498
  return;
413
499
  }
414
500
  return Object.is(number, -0) ? "0" : String(number);
@@ -550,7 +636,7 @@ const reservedBindings = /* @__PURE__ */ new Set([
550
636
  "with",
551
637
  "yield"
552
638
  ]);
553
- const excludedFamilies = [
639
+ const unsupportedSnapshotFamilies = [
554
640
  ["views", "views"],
555
641
  ["sequences", "sequences"],
556
642
  ["enums", "enums"],
@@ -560,7 +646,7 @@ const excludedFamilies = [
560
646
  ["routines", "routines"],
561
647
  ["partitions", "partitions"],
562
648
  ["policies", "policies"],
563
- ["extensionObjects", "extension objects"],
649
+ ["extensions", "extensions"],
564
650
  ["deferredObjects", "deferred objects"],
565
651
  ["opaqueObjects", "opaque objects"],
566
652
  ["comments", "comments"],
@@ -578,7 +664,7 @@ const excludedFamilies = [
578
664
  * expressions use dialect-tagged schema data, checks use `catalogCheck()`, and foreign keys use
579
665
  * lazy `catalogForeignKey()` targets. Foreign keys must have equal local and target arity and
580
666
  * target an exact primary key, strict unique key, or candidate index. Nullable database UNIQUE
581
- * constraints do not provide that candidate-key proof and fail generation. Application output,
667
+ * constraints and indexes with lossy candidate-key facts do not provide that proof. Application output,
582
668
  * insert, and update types default independently to `unknown`. The generator may attach a
583
669
  * conservative SQL semantic domain, and {@link SchemaCodegenOptions.mapColumn} can override either
584
670
  * type axis with a fixed token. Naming callbacks can replace suggested camelCase IDs, but they
@@ -599,8 +685,8 @@ function generateSchemaSource(input, options = {}) {
599
685
  diagnostics.push(errorDiagnostic("invalid-input", "Code generation requires an introspection result", []));
600
686
  return failure(diagnostics);
601
687
  }
688
+ if (!validateOptions(options, diagnostics)) return failure(diagnostics);
602
689
  diagnostics.push(...input.diagnostics);
603
- appendExcludedFamilyDiagnostics(input.catalog, diagnostics);
604
690
  if (!input.ok) {
605
691
  diagnostics.push(errorDiagnostic("invalid-input", "Code generation requires a successful introspection result", []));
606
692
  return failure(diagnostics);
@@ -613,6 +699,8 @@ function generateSchemaSource(input, options = {}) {
613
699
  return failure(diagnostics);
614
700
  }
615
701
  const snapshot = decoded.value;
702
+ appendUnsupportedSnapshotFamilyDiagnostics(snapshot, diagnostics);
703
+ if (hasErrors(diagnostics)) return failure(diagnostics);
616
704
  validateInputEnvelope(input.catalog, snapshot, diagnostics);
617
705
  validatePhysicalFacts(input.catalog, snapshot, diagnostics);
618
706
  validateSchemaProofs(snapshot, diagnostics);
@@ -641,7 +729,7 @@ function validateInputEnvelope(catalog, snapshot, diagnostics) {
641
729
  "dialect",
642
730
  "version"
643
731
  ]));
644
- if (snapshot.namespace !== catalog.namespace.name) diagnostics.push(errorDiagnostic("unsupported-snapshot", "Snapshot and catalog namespaces do not match", ["snapshot", "namespace"], void 0, [[
732
+ if (snapshot.namespace.name !== catalog.namespace.name) diagnostics.push(errorDiagnostic("unsupported-snapshot", "Snapshot and catalog namespaces do not match", ["snapshot", "namespace"], void 0, [[
645
733
  "catalog",
646
734
  "namespace",
647
735
  "name"
@@ -679,8 +767,7 @@ function validateSchemaProofs(snapshot, diagnostics) {
679
767
  }
680
768
  for (const index of table.indexes) {
681
769
  if (!index.candidateKey) continue;
682
- const candidateColumns = candidateIndexColumns(index);
683
- 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`, [
770
+ if (!hasCandidateKeyShape(index, columns)) diagnostics.push(errorDiagnostic("unrepresentable-fact", `Index "${index.physicalName}" is marked as a candidate key without an exact non-null column key`, [
684
771
  ...path,
685
772
  "indexes",
686
773
  index.id,
@@ -702,7 +789,7 @@ function validateSchemaProofs(snapshot, diagnostics) {
702
789
  ]));
703
790
  continue;
704
791
  }
705
- const target = tables.get(constraint.target.table);
792
+ const target = tables.get(constraint.target.table.id);
706
793
  if (target === void 0) continue;
707
794
  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`, [
708
795
  ...constraintPath,
@@ -716,21 +803,11 @@ function hasCandidateKey(table, targetColumns) {
716
803
  const columns = new Map(table.columns.map((column) => [column.id, column]));
717
804
  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;
718
805
  return table.indexes.some((index) => {
719
- if (!index.candidateKey) return false;
720
- const candidateColumns = candidateIndexColumns(index);
721
- return candidateColumns !== void 0 && candidateColumns.every((column) => columns.get(column)?.nullable === false) && sameColumns(candidateColumns, targetColumns);
806
+ if (!isCandidateKeyIndex(index, columns)) return false;
807
+ const candidateColumns = candidateKeyIndexColumns(index);
808
+ return candidateColumns !== void 0 && sameColumns(candidateColumns, targetColumns);
722
809
  });
723
810
  }
724
- function candidateIndexColumns(index) {
725
- if (!index.candidateKey || !index.unique || index.predicate !== void 0) return;
726
- const columns = [];
727
- for (const term of index.terms) {
728
- const expression = term.kind === "order" ? term.expression : term;
729
- if (expression.kind !== "column") return;
730
- columns.push(expression.column);
731
- }
732
- return columns.length > 0 ? columns : void 0;
733
- }
734
811
  function sameColumns(left, right) {
735
812
  return left.length === right.length && left.every((column, index) => column === right[index]);
736
813
  }
@@ -792,7 +869,7 @@ function resolveTable(snapshot, catalogTable, tableName, catalog, options, diagn
792
869
  ], diagnostics);
793
870
  const columns = [];
794
871
  const columnNames = /* @__PURE__ */ new Map();
795
- for (const column of [...snapshot.columns].sort(comparePhysicalName)) {
872
+ for (const column of [...snapshot.columns].sort(comparePhysicalPosition)) {
796
873
  const path = [
797
874
  "snapshot",
798
875
  "tables",
@@ -855,7 +932,7 @@ function resolveTable(snapshot, catalogTable, tableName, catalog, options, diagn
855
932
  const constraints = resolveObjects(snapshot.constraints, "constraint", snapshot, tableName, options, diagnostics);
856
933
  const indexes = resolveObjects(snapshot.indexes, "index", snapshot, tableName, options, diagnostics);
857
934
  if (hasErrors(diagnostics)) return;
858
- const orderedColumns = columns.sort(compareName);
935
+ const orderedColumns = columns;
859
936
  return {
860
937
  name: tableName,
861
938
  snapshot,
@@ -943,9 +1020,9 @@ function resolveColumnMapping(catalog, tablePhysicalName, tableName, column, col
943
1020
  }
944
1021
  function validateResolvedDomains(schema, diagnostics) {
945
1022
  for (const table of schema.tables) for (const index of table.indexes) for (const term of index.snapshot.terms) {
946
- const ordered = term.kind === "order" ? term : void 0;
947
- if (ordered?.expression.kind !== "column") continue;
948
- const column = table.columnsBySnapshotId.get(ordered.expression.column);
1023
+ const ordered = term.direction !== void 0 || term.nulls !== void 0 ? term : void 0;
1024
+ if (ordered?.kind !== "column") continue;
1025
+ const column = table.columnsBySnapshotId.get(ordered.column);
949
1026
  if (column === void 0 || orderableDomains.has(column.sqlDomain)) continue;
950
1027
  if (column.explicitSqlDomain) diagnostics.push(errorDiagnostic("invalid-option", `SQL domain "${column.sqlDomain}" cannot represent an ordered index term`, [
951
1028
  "snapshot",
@@ -959,7 +1036,7 @@ function validateResolvedDomains(schema, diagnostics) {
959
1036
  }
960
1037
  for (const table of schema.tables) for (const constraint of table.constraints) {
961
1038
  if (constraint.snapshot.kind !== "foreign-key") continue;
962
- const targetTable = schema.tablesBySnapshotId.get(constraint.snapshot.target.table);
1039
+ const targetTable = schema.tablesBySnapshotId.get(constraint.snapshot.target.table.id);
963
1040
  if (targetTable === void 0) continue;
964
1041
  for (const [index, localId] of constraint.snapshot.columns.entries()) {
965
1042
  const targetId = constraint.snapshot.target.columns[index];
@@ -1065,47 +1142,41 @@ function toPhysicalFacts(snapshot) {
1065
1142
  const columnNames = new Map(snapshot.tables.map((table) => [table.id, new Map(table.columns.map((column) => [column.id, column.physicalName]))]));
1066
1143
  return {
1067
1144
  dialect: snapshot.dialect,
1068
- namespace: snapshot.namespace ?? null,
1145
+ namespace: snapshot.namespace.name,
1069
1146
  tables: [...snapshot.tables].sort(comparePhysicalName).map((table) => ({
1070
1147
  physicalName: table.physicalName,
1071
- columns: [...table.columns].sort(comparePhysicalName).map((column) => ({
1072
- ...column,
1073
- id: column.physicalName
1074
- })),
1148
+ ...table.dialect === void 0 ? {} : { dialect: stripEvidence(table.dialect) },
1149
+ columns: [...table.columns].sort(comparePhysicalPosition).map((column) => stripObjectIdentity(column)),
1075
1150
  constraints: [...table.constraints].sort(comparePhysicalName).map((constraint) => physicalConstraint(constraint, table, tableById, columnNames)),
1076
1151
  indexes: [...table.indexes].sort(comparePhysicalName).map((index) => physicalIndex(index, table, columnNames))
1077
1152
  }))
1078
1153
  };
1079
1154
  }
1080
1155
  function physicalConstraint(constraint, table, tables, columns) {
1081
- if (constraint.kind === "check") return {
1082
- ...constraint,
1083
- id: constraint.physicalName
1084
- };
1156
+ if (constraint.kind === "check") return stripObjectIdentity(constraint);
1085
1157
  const localColumns = constraint.columns.map((id) => columns.get(table.id)?.get(id) ?? `missing:${id}`);
1086
1158
  if (constraint.kind !== "foreign-key") return {
1087
- ...constraint,
1088
- id: constraint.physicalName,
1089
- columns: localColumns
1159
+ ...stripObjectIdentity(constraint),
1160
+ columns: localColumns,
1161
+ ...constraint.backingIndex === void 0 ? {} : { backingIndex: physicalReference(constraint.backingIndex, table.indexes) }
1090
1162
  };
1091
- const targetTable = tables.get(constraint.target.table);
1163
+ const targetTable = tables.get(constraint.target.table.id);
1092
1164
  return {
1093
- ...constraint,
1094
- id: constraint.physicalName,
1165
+ ...stripObjectIdentity(constraint),
1095
1166
  columns: localColumns,
1096
1167
  target: {
1097
- table: targetTable?.physicalName ?? `missing:${constraint.target.table}`,
1098
- columns: constraint.target.columns.map((id) => columns.get(constraint.target.table)?.get(id) ?? `missing:${id}`)
1168
+ table: targetTable?.physicalName ?? `missing:${constraint.target.table.id}`,
1169
+ columns: constraint.target.columns.map((id) => columns.get(constraint.target.table.id)?.get(id) ?? `missing:${id}`)
1099
1170
  }
1100
1171
  };
1101
1172
  }
1102
1173
  function physicalIndex(index, table, columns) {
1103
1174
  const names = columns.get(table.id);
1104
1175
  return {
1105
- ...index,
1106
- id: index.physicalName,
1107
- terms: index.terms.map((term) => physicalIndexTerm(term, names)),
1108
- ...index.includedColumns === void 0 ? {} : { includedColumns: index.includedColumns.map((id) => names?.get(id) ?? `missing:${id}`) }
1176
+ ...stripObjectIdentity(index),
1177
+ terms: [...index.terms].sort((left, right) => left.position - right.position).map((term) => physicalIndexTerm(term, names)),
1178
+ ...index.includedColumns === void 0 ? {} : { includedColumns: index.includedColumns.map((id) => names?.get(id) ?? `missing:${id}`) },
1179
+ ...index.backingConstraint === void 0 ? {} : { backingConstraint: physicalReference(index.backingConstraint, table.constraints) }
1109
1180
  };
1110
1181
  }
1111
1182
  function physicalIndexTerm(term, columns) {
@@ -1114,28 +1185,33 @@ function physicalIndexTerm(term, columns) {
1114
1185
  column: columns?.get(term.column) ?? `missing:${term.column}`
1115
1186
  };
1116
1187
  if (term.kind === "expression") return term;
1117
- return {
1118
- ...term,
1119
- expression: term.expression.kind === "column" ? {
1120
- ...term.expression,
1121
- column: columns?.get(term.expression.column) ?? `missing:${term.expression.column}`
1122
- } : term.expression
1123
- };
1188
+ return term;
1124
1189
  }
1125
- function appendExcludedFamilyDiagnostics(catalog, diagnostics) {
1126
- if (catalog === void 0) return;
1127
- for (const [property, label] of excludedFamilies) {
1128
- const values = catalog[property];
1129
- if (values === void 0 || values.length === 0) continue;
1130
- diagnostics.push({
1131
- severity: "warning",
1132
- code: "excluded-object-family",
1133
- message: `Snapshot v1 source generation excludes ${values.length} ${label}`,
1134
- path: Object.freeze(["catalog", property]),
1135
- remediation: "Use the complete normalized catalog or Snapshot v2 for this object family."
1136
- });
1190
+ function appendUnsupportedSnapshotFamilyDiagnostics(snapshot, diagnostics) {
1191
+ for (const [property, label] of unsupportedSnapshotFamilies) {
1192
+ const values = snapshot[property];
1193
+ if (values.length === 0) continue;
1194
+ diagnostics.push(errorDiagnostic("excluded-object-family", `Snapshot v1 source generation cannot represent ${values.length} ${label}`, ["snapshot", property], "Generate source from a snapshot containing tables only."));
1137
1195
  }
1138
1196
  }
1197
+ function stripObjectIdentity(value) {
1198
+ const stripped = stripEvidence(value);
1199
+ if (typeof stripped !== "object" || stripped === null || Array.isArray(stripped)) return {};
1200
+ const { id: _id, ...facts } = stripped;
1201
+ return facts;
1202
+ }
1203
+ function stripEvidence(value) {
1204
+ if (Array.isArray(value)) return value.map(stripEvidence);
1205
+ if (typeof value !== "object" || value === null) return value;
1206
+ return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).flatMap(([key, item]) => key === "provenance" || key === "physicalReference" ? [] : [[key, stripEvidence(item)]]));
1207
+ }
1208
+ function physicalReference(reference, objects) {
1209
+ const target = objects.find((object) => object.id === reference.id);
1210
+ return {
1211
+ ...reference,
1212
+ id: target?.physicalName ?? `missing:${reference.id}`
1213
+ };
1214
+ }
1139
1215
  function groupUnique(values, keyOf, path, diagnostics) {
1140
1216
  const result = /* @__PURE__ */ new Map();
1141
1217
  for (const [index, value] of values.entries()) {
@@ -1182,12 +1258,26 @@ function hasErrors(diagnostics) {
1182
1258
  function comparePhysicalName(left, right) {
1183
1259
  return left.physicalName < right.physicalName ? -1 : left.physicalName > right.physicalName ? 1 : 0;
1184
1260
  }
1261
+ function comparePhysicalPosition(left, right) {
1262
+ return left.ordinalPosition - right.ordinalPosition || comparePhysicalName(left, right);
1263
+ }
1185
1264
  function compareName(left, right) {
1186
1265
  return left.name < right.name ? -1 : left.name > right.name ? 1 : 0;
1187
1266
  }
1188
1267
  function isOptions(value) {
1189
1268
  return typeof value === "object" && value !== null && !Array.isArray(value);
1190
1269
  }
1270
+ function validateOptions(options, diagnostics) {
1271
+ let valid = true;
1272
+ for (const key of ["naming", "mapColumn"]) {
1273
+ const callback = options[key];
1274
+ if (callback !== void 0 && typeof callback !== "function") {
1275
+ diagnostics.push(errorDiagnostic("invalid-option", `${key} must be a function`, ["options", key]));
1276
+ valid = false;
1277
+ }
1278
+ }
1279
+ return valid;
1280
+ }
1191
1281
  function isIntrospectionResult(value) {
1192
1282
  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);
1193
1283
  }
@@ -0,0 +1,116 @@
1
+ import { U as resultValue, f as resolveColumnBehavior } from "./column-DDRvD7SF.mjs";
2
+ //#region src/schema/column.ts
3
+ /** Create an immutable portable physical storage descriptor. */
4
+ function portableStorage(type) {
5
+ return Object.freeze({
6
+ kind: "portable",
7
+ type
8
+ });
9
+ }
10
+ function nativeStorage(dialectOrOptions, type) {
11
+ const dialect = typeof dialectOrOptions === "string" ? dialectOrOptions : dialectOrOptions.dialect;
12
+ const declaration = typeof dialectOrOptions === "string" ? type : "type" in dialectOrOptions ? dialectOrOptions.type : dialectOrOptions.declaration;
13
+ return Object.freeze({
14
+ kind: "native",
15
+ dialect,
16
+ type: declaration
17
+ });
18
+ }
19
+ function narrowColumnType() {
20
+ return this;
21
+ }
22
+ function withPortableCast(definition, type) {
23
+ return Object.freeze({
24
+ ...definition,
25
+ castTarget: Object.freeze({
26
+ kind: "portable-cast",
27
+ type
28
+ })
29
+ });
30
+ }
31
+ function withPortableStorage(definition, type) {
32
+ return Object.freeze({
33
+ ...definition,
34
+ storage: portableStorage(type),
35
+ sqlType: type === "numeric" ? "decimal" : type
36
+ });
37
+ }
38
+ function column(options) {
39
+ return Object.freeze({
40
+ definitionKind: "column",
41
+ nullable: options?.nullable === true,
42
+ ...resolveColumnBehavior(options ?? {}),
43
+ sqlName: options?.sqlName,
44
+ ...options?.sqlType === void 0 ? {} : { sqlType: options.sqlType },
45
+ storage: options?.storage ? Object.freeze({ ...options.storage }) : void 0,
46
+ dialect: options?.dialect,
47
+ ...options?.decode === void 0 && options?.codec === void 0 ? {} : { resultDecoder: options?.decode ?? options?.codec?.fromDriver },
48
+ ...options?.codec === void 0 ? {} : {
49
+ parameterEncoder: options.codec.toDriver,
50
+ columnCodec: Object.freeze({ ...options.codec })
51
+ },
52
+ $type: narrowColumnType,
53
+ castTarget: options?.castType ? Object.freeze({
54
+ kind: "named-cast",
55
+ typeName: options.castType
56
+ }) : void 0
57
+ });
58
+ }
59
+ /** Resolve the runtime result metadata carried by a column definition. */
60
+ function columnResultValue(definition) {
61
+ const sqlType = columnSqlType(definition);
62
+ return resultValue(sqlType === "boolean" || sqlType === "date" || sqlType === "timestamp" || sqlType === "json" || sqlType === "bigint" ? sqlType : void 0, definition.resultDecoder, sqlType);
63
+ }
64
+ /** Resolve a column's explicit runtime semantic SQL domain for driver-facing metadata. */
65
+ function columnSqlType(definition) {
66
+ return definition.sqlType;
67
+ }
68
+ /** Apply a live column codec while preserving SQL NULL unchanged. */
69
+ function encodeColumnParameter(definition, value) {
70
+ return value === null || definition.parameterEncoder === void 0 ? value : definition.parameterEncoder(value);
71
+ }
72
+ function nativeColumn(storageOrDialect, typeOrOptions, maybeOptions) {
73
+ const storage = typeof storageOrDialect === "string" ? nativeStorage(storageOrDialect, typeOrOptions) : storageOrDialect;
74
+ return column({
75
+ ...typeof storageOrDialect === "string" ? maybeOptions : typeOrOptions,
76
+ storage
77
+ });
78
+ }
79
+ function nullable(definition) {
80
+ return Object.freeze({
81
+ ...definition,
82
+ nullable: true
83
+ });
84
+ }
85
+ function integer(options) {
86
+ return withPortableStorage(withPortableCast(column(options), "integer"), "integer");
87
+ }
88
+ function numeric(options) {
89
+ return withPortableStorage(withPortableCast(column(options), "decimal"), "numeric");
90
+ }
91
+ function text(options) {
92
+ return withPortableStorage(withPortableCast(column(options), "text"), "text");
93
+ }
94
+ function boolean(options) {
95
+ return withPortableStorage(withPortableCast(column(options), "boolean"), "boolean");
96
+ }
97
+ function date(options) {
98
+ return withPortableStorage(withPortableCast(column(options), "date"), "date");
99
+ }
100
+ function timestamp(options) {
101
+ return withPortableStorage(withPortableCast(column(options), "timestamp"), "timestamp");
102
+ }
103
+ function uuid(options) {
104
+ return withPortableStorage(withPortableCast(column(options), "uuid"), "uuid");
105
+ }
106
+ function json(options) {
107
+ return withPortableStorage(withPortableCast(column(options), "json"), "json");
108
+ }
109
+ function bigint(options) {
110
+ return withPortableStorage(withPortableCast(column(options), "bigint"), "bigint");
111
+ }
112
+ function binary(options) {
113
+ return withPortableStorage(withPortableCast(column(options), "binary"), "binary");
114
+ }
115
+ //#endregion
116
+ export { timestamp as _, columnResultValue as a, encodeColumnParameter as c, nativeColumn as d, nativeStorage as f, text as g, portableStorage as h, column as i, integer as l, numeric as m, binary as n, columnSqlType as o, nullable as p, boolean as r, date as s, bigint as t, json as u, uuid as v };