qubu 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/codegen.mjs +1 -1
  2. package/dist/column-Da37jYSD.mjs +309 -0
  3. package/dist/column-r1Y4ivwt.mjs +327 -0
  4. package/dist/constraints-YGyNPQ_z.mjs +208 -0
  5. package/dist/core.mjs +3 -3
  6. package/dist/index-B2rZf3-2.d.mts +32 -0
  7. package/dist/index.mjs +9 -7
  8. package/dist/introspection.mjs +1 -1
  9. package/dist/{on-conflict-jPsl9l0K.mjs → on-conflict-DZQ85f1t.mjs} +3 -2
  10. package/dist/postgres.mjs +3 -3
  11. package/dist/registry-BRcUuazJ.mjs +256 -0
  12. package/dist/{relational-x3BDVX9e.mjs → relational-CxnLCqZQ.mjs} +2 -2
  13. package/dist/schema.mjs +6 -4
  14. package/dist/{sqlite-CsIUtZ2Q.mjs → serialize-BN07IK0v.mjs} +3 -317
  15. package/dist/serialize-CEIIlWhC.d.mts +66 -0
  16. package/dist/snapshot/mysql.d.mts +17 -0
  17. package/dist/snapshot/mysql.mjs +356 -0
  18. package/dist/snapshot/postgres.d.mts +17 -0
  19. package/dist/snapshot/postgres.mjs +237 -0
  20. package/dist/snapshot/sqlite.d.mts +25 -0
  21. package/dist/snapshot/sqlite.mjs +321 -0
  22. package/dist/{snapshot-BSraiLtH.mjs → snapshot-Xam8-q0j.mjs} +1 -1
  23. package/dist/snapshot.d.mts +3 -2
  24. package/dist/snapshot.mjs +2 -583
  25. package/dist/{source-C4Vmu5bb.mjs → source-SqrKWjFJ.mjs} +2 -1
  26. package/dist/sqlite.mjs +2 -2
  27. package/dist/{table-DLQ7YWth.mjs → table-BwflqeAj.mjs} +3 -2
  28. package/dist/{types-CTENnDh9.mjs → types-BIJsj2fJ.mjs} +3 -2
  29. package/dist/{value-BEEj_Ayd.mjs → value-D14I_XgL.mjs} +1 -1
  30. package/docs/dialects-and-execution.md +34 -0
  31. package/docs/migrations/index.md +7 -7
  32. package/docs/reference/mysql-snapshot.md +5 -5
  33. package/docs/reference/postgres-snapshot.md +7 -7
  34. package/docs/reference/sqlite-snapshot.md +5 -5
  35. package/docs/reference/supported-surface.md +40 -32
  36. package/docs/schema/code-generation.md +2 -2
  37. package/docs/schema/ddl-emission.md +1 -1
  38. package/docs/schema/snapshots.md +12 -0
  39. package/package.json +13 -1
  40. package/dist/column-DmazTL67.mjs +0 -633
  41. package/dist/index-C-480HmV.d.mts +0 -146
  42. package/dist/registry-BGqa05et.mjs +0 -461
@@ -1,146 +0,0 @@
1
- import { T as SnapshotJsonValue, Tr as SchemaDialect, Ur as Schema, f as SnapshotDiagnostic, i as SchemaSnapshotInput, l as SnapshotCreateResult, m as SnapshotDialect, n as SchemaSnapshot, r as SchemaSnapshotAdapter, u as SnapshotDecodeResult } from "./types-C0VkiwpR.mjs";
2
- import { n as CompleteSchemaSnapshotInput, t as CompleteSchemaSnapshot, u as CompleteSnapshotDecodeResult } from "./complete-types-CY0KbzNw.mjs";
3
- //#region src/snapshot/canonical.d.ts
4
- /** Error raised when a value cannot cross the canonical JSON boundary. */
5
- declare class SnapshotCanonicalError extends TypeError {
6
- readonly path: readonly (string | number)[];
7
- constructor(message: string, path?: readonly (string | number)[]);
8
- }
9
- /** Convert a JavaScript value to the explicit JSON-safe snapshot domain. */
10
- declare function toSnapshotJsonValue(value: unknown, path?: readonly (string | number)[], seen?: WeakSet<object>): SnapshotJsonValue;
11
- /** Render a JSON-safe value with deterministic scalar and object handling. */
12
- declare function canonicalJson(value: SnapshotJsonValue): string;
13
- /** Canonical JSON for a schema snapshot after fixed-order reconstruction. */
14
- declare function encodeCanonicalSnapshot(snapshot: SchemaSnapshot): string;
15
- /**
16
- * Compute a small deterministic content fingerprint for cache keys and diagnostics. It is
17
- * deliberately not an entity identity, signature, or migration lineage.
18
- */
19
- declare function schemaSnapshotFingerprint(snapshot: SchemaSnapshotInput | string): string;
20
- //#endregion
21
- //#region src/snapshot/complete.d.ts
22
- /** Error raised by throwing APIs after collecting strict v2 diagnostics. */
23
- declare class CompleteSnapshotValidationError extends TypeError {
24
- readonly name = "CompleteSnapshotValidationError";
25
- readonly diagnostics: readonly SnapshotDiagnostic[];
26
- readonly issues: readonly SnapshotDiagnostic[];
27
- constructor(diagnostics: readonly SnapshotDiagnostic[]);
28
- }
29
- /** Decode and strictly validate a complete Snapshot v2 JSON value. */
30
- declare function decodeCompleteSchemaSnapshot(input: string | unknown): CompleteSnapshotDecodeResult;
31
- /** Validate a complete snapshot and throw one structured error on failure. */
32
- declare function assertCompleteSchemaSnapshot(input: CompleteSchemaSnapshotInput | string): CompleteSchemaSnapshot;
33
- /** Return a fixed-order, deeply immutable complete snapshot. */
34
- declare function canonicalizeCompleteSchemaSnapshot(input: CompleteSchemaSnapshotInput): CompleteSchemaSnapshot;
35
- /** Encode a complete snapshot with deterministic property and array order. */
36
- declare function encodeCompleteSchemaSnapshot(snapshot: CompleteSchemaSnapshot): string;
37
- /** Compute the deterministic content fingerprint for a complete Snapshot v2. */
38
- declare function completeSchemaSnapshotFingerprint(snapshot: CompleteSchemaSnapshotInput | string): string;
39
- /** Numbered aliases for tooling that keeps v1 and v2 side by side. */
40
- declare const decodeSchemaSnapshotV2: typeof decodeCompleteSchemaSnapshot;
41
- declare const assertSchemaSnapshotV2: typeof assertCompleteSchemaSnapshot;
42
- declare const encodeSchemaSnapshotV2: typeof encodeCompleteSchemaSnapshot;
43
- declare const canonicalizeSchemaSnapshotV2: typeof canonicalizeCompleteSchemaSnapshot;
44
- declare const schemaSnapshotV2Fingerprint: typeof completeSchemaSnapshotFingerprint;
45
- declare const decodeCompleteSnapshot: typeof decodeCompleteSchemaSnapshot;
46
- declare const assertCompleteSnapshot: typeof assertCompleteSchemaSnapshot;
47
- declare const encodeCompleteSnapshot: typeof encodeCompleteSchemaSnapshot;
48
- declare const fingerprintCompleteSchemaSnapshot: typeof completeSchemaSnapshotFingerprint;
49
- //#endregion
50
- //#region src/snapshot/decode.d.ts
51
- /** Error raised by throwing snapshot APIs after collecting diagnostics. */
52
- declare class SnapshotValidationError extends TypeError {
53
- readonly name: string;
54
- readonly diagnostics: readonly SnapshotDiagnostic[];
55
- readonly issues: readonly SnapshotDiagnostic[];
56
- constructor(diagnostics: readonly SnapshotDiagnostic[]);
57
- }
58
- /** Decode and strictly validate a JSON string or parsed snapshot value. */
59
- declare function decodeSchemaSnapshot(input: string | unknown): SnapshotDecodeResult;
60
- /** Validate a snapshot and throw one structured error if it is malformed. */
61
- declare function assertSchemaSnapshot(input: SchemaSnapshotInput | string): SchemaSnapshot;
62
- /** Return a fixed-order, deeply immutable copy of a valid snapshot. */
63
- declare function canonicalizeSchemaSnapshot(input: SchemaSnapshotInput): SchemaSnapshot;
64
- //#endregion
65
- //#region src/snapshot/serialize.d.ts
66
- /** Error raised when a live Qubu schema cannot be represented as a snapshot. */
67
- declare class SnapshotSerializationError extends SnapshotValidationError {
68
- readonly name = "SnapshotSerializationError";
69
- }
70
- /** Options for the neutral traversal and a future dialect adapter. */
71
- interface SchemaSnapshotOptions {
72
- readonly adapter?: SchemaSnapshotAdapter;
73
- readonly dialect?: SchemaDialect;
74
- /** Explicit hook overrides retained for custom tooling integrations. */
75
- readonly expressionEncoder?: SchemaDialect["schema"]["encodeExpression"];
76
- readonly storageEncoder?: SchemaDialect["schema"]["encodeStorage"];
77
- readonly extensionEncoder?: SchemaDialect["schema"]["encodeDialectExtension"];
78
- readonly namingPolicy?: {
79
- readonly name: string;
80
- readonly version?: number;
81
- };
82
- }
83
- /** Create a neutral or adapter-owned immutable snapshot from a live schema. */
84
- declare function createSchemaSnapshot<TSchema extends Schema<any>>(schema: TSchema, options?: SchemaSnapshotOptions): SchemaSnapshot;
85
- /** Non-throwing form of {@link createSchemaSnapshot}. */
86
- declare function tryCreateSchemaSnapshot<TSchema extends Schema<any>>(schema: TSchema, options?: SchemaSnapshotOptions): SnapshotCreateResult;
87
- /** Encode a valid snapshot using fixed property order and canonical JSON. */
88
- declare function encodeSchemaSnapshot(snapshot: SchemaSnapshot): string;
89
- /** Alias used by tooling packages that call the operation serialization. */
90
- declare const serializeSchema: typeof createSchemaSnapshot;
91
- /** Alias for the canonical snapshot encoder. */
92
- declare const encodeSnapshot: typeof encodeSchemaSnapshot;
93
- /** Alias for the canonical snapshot fingerprint helper. */
94
- declare const fingerprintSchemaSnapshot: typeof schemaSnapshotFingerprint;
95
- //#endregion
96
- //#region src/snapshot/postgres.d.ts
97
- /** PostgreSQL's v1 snapshot extension identity. */
98
- declare const postgresSnapshotDialect: SnapshotDialect;
99
- /** PostgreSQL's query dialect plus its schema metadata behavior. */
100
- declare const postgresSchemaDialect: SchemaDialect<"ilike" | "json" | "on-conflict" | "row-locking">;
101
- /** Snapshot adapter retaining the historical adapter-shaped entry point. */
102
- declare const postgresSnapshotAdapter: SchemaSnapshotAdapter<"ilike" | "json" | "on-conflict" | "row-locking">;
103
- /** Create a canonical PostgreSQL schema snapshot. */
104
- declare function createPostgresSchemaSnapshot<TSchema extends Schema<any>>(schema: TSchema, options?: PostgresSnapshotOptions): SchemaSnapshot;
105
- /** Return PostgreSQL capability diagnostics without throwing. */
106
- declare function tryCreatePostgresSchemaSnapshot<TSchema extends Schema<any>>(schema: TSchema, options?: PostgresSnapshotOptions): SnapshotCreateResult;
107
- /** Options accepted by the PostgreSQL snapshot convenience functions. */
108
- type PostgresSnapshotOptions = Omit<SchemaSnapshotOptions, "adapter" | "dialect">;
109
- //#endregion
110
- //#region src/snapshot/sqlite.d.ts
111
- /** SQLite's v1 snapshot extension identity. */
112
- declare const sqliteSnapshotDialect: SnapshotDialect;
113
- /** SQLite's five declared-type affinity categories. */
114
- type SqliteStorageAffinity = "blob" | "integer" | "numeric" | "real" | "text";
115
- /**
116
- * Resolve SQLite's declared-type affinity without changing the declaration. SQLite applies these
117
- * ordered substring rules at table creation time; the returned affinity is useful metadata, not a
118
- * replacement for the exact type.
119
- */
120
- declare function sqliteStorageAffinity(declaration: string): SqliteStorageAffinity;
121
- /** SQLite's query dialect plus its schema metadata behavior. */
122
- declare const sqliteSchemaDialect: SchemaDialect<"json" | "on-conflict">;
123
- /** Snapshot adapter retaining the historical adapter-shaped entry point. */
124
- declare const sqliteSnapshotAdapter: SchemaSnapshotAdapter<"json" | "on-conflict">;
125
- /** Create a canonical SQLite schema snapshot. */
126
- declare function createSqliteSchemaSnapshot<TSchema extends Schema<any>>(schema: TSchema, options?: SqliteSnapshotOptions): SchemaSnapshot;
127
- /** Return SQLite capability diagnostics without throwing. */
128
- declare function tryCreateSqliteSchemaSnapshot<TSchema extends Schema<any>>(schema: TSchema, options?: SqliteSnapshotOptions): SnapshotCreateResult;
129
- /** Options accepted by the SQLite snapshot convenience functions. */
130
- type SqliteSnapshotOptions = Omit<SchemaSnapshotOptions, "adapter" | "dialect">;
131
- //#endregion
132
- //#region src/snapshot/mysql.d.ts
133
- /** MySQL's v1 snapshot extension identity. */
134
- declare const mysqlSnapshotDialect: SnapshotDialect;
135
- /** MySQL's query dialect plus its schema metadata behavior. */
136
- declare const mysqlSchemaDialect: SchemaDialect<"json" | "row-locking">;
137
- /** Snapshot adapter retaining the historical adapter-shaped entry point. */
138
- declare const mysqlSnapshotAdapter: SchemaSnapshotAdapter<"json" | "row-locking">;
139
- /** Create a canonical MySQL schema snapshot. */
140
- declare function createMysqlSchemaSnapshot<TSchema extends Schema<any>>(schema: TSchema, options?: MysqlSnapshotOptions): SchemaSnapshot;
141
- /** Return MySQL capability diagnostics without throwing. */
142
- declare function tryCreateMysqlSchemaSnapshot<TSchema extends Schema<any>>(schema: TSchema, options?: MysqlSnapshotOptions): SnapshotCreateResult;
143
- /** Options accepted by the MySQL snapshot convenience functions. */
144
- type MysqlSnapshotOptions = Omit<SchemaSnapshotOptions, "adapter" | "dialect">;
145
- //#endregion
146
- export { assertSchemaSnapshot as A, decodeCompleteSchemaSnapshot as B, createSchemaSnapshot as C, serializeSchema as D, fingerprintSchemaSnapshot as E, assertCompleteSnapshot as F, encodeSchemaSnapshotV2 as G, decodeSchemaSnapshotV2 as H, assertSchemaSnapshotV2 as I, SnapshotCanonicalError as J, fingerprintCompleteSchemaSnapshot as K, canonicalizeCompleteSchemaSnapshot as L, decodeSchemaSnapshot as M, CompleteSnapshotValidationError as N, tryCreateSchemaSnapshot as O, assertCompleteSchemaSnapshot as P, toSnapshotJsonValue as Q, canonicalizeSchemaSnapshotV2 as R, SnapshotSerializationError as S, encodeSnapshot as T, encodeCompleteSchemaSnapshot as U, decodeCompleteSnapshot as V, encodeCompleteSnapshot as W, encodeCanonicalSnapshot as X, canonicalJson as Y, schemaSnapshotFingerprint as Z, postgresSchemaDialect as _, mysqlSnapshotDialect as a, tryCreatePostgresSchemaSnapshot as b, SqliteStorageAffinity as c, sqliteSnapshotAdapter as d, sqliteSnapshotDialect as f, createPostgresSchemaSnapshot as g, PostgresSnapshotOptions as h, mysqlSnapshotAdapter as i, canonicalizeSchemaSnapshot as j, SnapshotValidationError as k, createSqliteSchemaSnapshot as l, tryCreateSqliteSchemaSnapshot as m, createMysqlSchemaSnapshot as n, tryCreateMysqlSchemaSnapshot as o, sqliteStorageAffinity as p, schemaSnapshotV2Fingerprint as q, mysqlSchemaDialect as r, SqliteSnapshotOptions as s, MysqlSnapshotOptions as t, sqliteSchemaDialect as u, postgresSnapshotAdapter as v, encodeSchemaSnapshot as w, SchemaSnapshotOptions as x, postgresSnapshotDialect as y, completeSchemaSnapshotFingerprint as z };
@@ -1,461 +0,0 @@
1
- import { t as standardDialect } from "./standard-DfcZEVOj.mjs";
2
- import { G as markSchemaExpression, M as dialectMismatchDiagnostic, N as freezeSchemaMetadata, U as makeSchemaExpression, V as isSchemaExpression, r as isColumnReference, z as snakeCaseIdentifier } from "./column-DmazTL67.mjs";
3
- import { n as isValueExpression } from "./value-BEEj_Ayd.mjs";
4
- //#region src/schema/indexes.ts
5
- /**
6
- * Validate portable and dialect-owned index facts for one target adapter. Unsupported features are
7
- * reported as data so a serializer can aggregate diagnostics instead of failing during traversal
8
- * with an opaque exception.
9
- */
10
- function validateIndexDialect(indexMetadata, dialect, path = ["index"]) {
11
- const diagnostics = [];
12
- const extension = indexMetadata.dialect;
13
- if (extension !== void 0) {
14
- const mismatch = dialectMismatchDiagnostic(extension, dialect, [...path, "dialect"]);
15
- if (mismatch !== void 0) diagnostics.push(mismatch);
16
- }
17
- if ((dialect === "sqlite" || dialect === "mysql") && indexMetadata.includedColumns !== void 0 && indexMetadata.includedColumns.length > 0) diagnostics.push({
18
- code: "unsupported-dialect-option",
19
- message: `${dialect} indexes do not support included columns`,
20
- path: [...path, "includedColumns"],
21
- dialect
22
- });
23
- const mysqlKeyBlockSize = extension?.dialect === "mysql" ? extension.keyBlockSize : void 0;
24
- if (mysqlKeyBlockSize !== void 0 && (!Number.isInteger(mysqlKeyBlockSize) || mysqlKeyBlockSize <= 0)) diagnostics.push({
25
- code: "unsupported-dialect-option",
26
- message: "MySQL index keyBlockSize must be a positive integer",
27
- path: [
28
- ...path,
29
- "dialect",
30
- "keyBlockSize"
31
- ],
32
- dialect
33
- });
34
- return Object.freeze(diagnostics);
35
- }
36
- /** Declare a portable column or expression index. */
37
- function index(terms, options) {
38
- const resolvedOptions = options ?? {};
39
- const candidateKey = resolvedOptions.unique === true && resolvedOptions.where === void 0 && terms.every((term) => {
40
- return ("orderKind" in term ? term.expression : term).expressionKind === "column";
41
- });
42
- return Object.freeze({
43
- kind: "index",
44
- terms: Object.freeze([...terms]),
45
- unique: resolvedOptions.unique === true,
46
- predicate: resolvedOptions.where,
47
- ...resolvedOptions.include !== void 0 ? { includedColumns: Object.freeze([...resolvedOptions.include]) } : {},
48
- ...resolvedOptions.physicalName !== void 0 ? { physicalName: resolvedOptions.physicalName } : {},
49
- ...resolvedOptions.dialect !== void 0 ? { dialect: freezeSchemaMetadata(resolvedOptions.dialect) } : {},
50
- candidateKey
51
- });
52
- }
53
- //#endregion
54
- //#region src/schema/expressions.ts
55
- /** Error raised before a schema expression can become persisted SQL. */
56
- var SchemaExpressionError = class extends TypeError {
57
- code;
58
- mode;
59
- constructor(code, message, mode) {
60
- super(message);
61
- this.name = "SchemaExpressionError";
62
- this.code = code;
63
- this.mode = mode;
64
- }
65
- };
66
- /**
67
- * Normalize only line endings. Whitespace, quoting, and every other byte of a raw schema expression
68
- * remain under the extension author's control.
69
- */
70
- function normalizeSchemaSql(sql) {
71
- return sql.replace(/\r\n?/g, "\n");
72
- }
73
- function schemaExpression(expression) {
74
- return markSchemaExpression(expression);
75
- }
76
- /**
77
- * Define an extension with the restricted schema context. This is the typed alternative to
78
- * {@link unsafeSchemaSql} for deterministic custom syntax.
79
- */
80
- function defineSchemaExpression(kind, render) {
81
- return makeSchemaExpression(kind, (context) => render(context));
82
- }
83
- function unsafeSchemaSql(dialectOrOptions, sql) {
84
- const dialect = typeof dialectOrOptions === "string" ? dialectOrOptions : dialectOrOptions.dialect;
85
- const source = typeof dialectOrOptions === "string" ? sql : dialectOrOptions.sql;
86
- if (!dialect) throw new TypeError("unsafeSchemaSql() requires a dialect tag");
87
- if (source === void 0) throw new TypeError("unsafeSchemaSql() requires SQL text");
88
- const normalized = normalizeSchemaSql(source);
89
- const expression = makeSchemaExpression("unsafe", (context) => context.append(normalized));
90
- return Object.freeze({
91
- ...expression,
92
- schemaSqlDialect: dialect,
93
- schemaSql: normalized
94
- });
95
- }
96
- /** Identify a dialect-tagged raw schema expression. */
97
- function isUnsafeSchemaSql(value) {
98
- return isSchemaExpression(value) && value.expressionKind === "unsafe" && typeof value.schemaSqlDialect === "string" && typeof value.schemaSql === "string";
99
- }
100
- function renderSchemaExpression(expression, optionsOrMode, dialectOption) {
101
- const options = typeof optionsOrMode === "string" ? {
102
- mode: optionsOrMode,
103
- dialect: dialectOption
104
- } : optionsOrMode;
105
- const dialect = options.dialect ?? standardDialect();
106
- if (!isSchemaExpression(expression)) throw new SchemaExpressionError("not-deterministic", "Only branded deterministic expressions can be rendered as schema SQL", options.mode);
107
- assertSupportedExpression(expression, options.mode);
108
- let text = "";
109
- const context = {
110
- dialect,
111
- projectionMode: "result",
112
- schemaMode: options.mode,
113
- append(value) {
114
- text += value;
115
- },
116
- parameter() {
117
- throw new SchemaExpressionError("parameter", "Schema expressions cannot render query parameters", options.mode);
118
- },
119
- literal(value) {
120
- text += renderSchemaLiteral(dialect, value, options.mode);
121
- },
122
- renderColumnReference(columnName) {
123
- if (options.mode === "default") throw new SchemaExpressionError("column-not-allowed", "Default expressions cannot reference table columns", options.mode);
124
- text += dialect.quoteIdentifier(columnName);
125
- },
126
- render(part) {
127
- renderSchemaPart(context, part, options.mode);
128
- },
129
- renderRelation() {
130
- throw new SchemaExpressionError("unsupported-expression", "Schema expressions cannot contain subqueries", options.mode);
131
- }
132
- };
133
- renderSchemaPart(context, expression, options.mode);
134
- return Object.freeze({
135
- text,
136
- parameters: Object.freeze([])
137
- });
138
- }
139
- /** Convenience form for callers that only need the SQL text. */
140
- function renderSchemaSql(expression, options) {
141
- return renderSchemaExpression(expression, options).text;
142
- }
143
- function renderSchemaPart(context, part, mode) {
144
- if (isValueExpression(part)) {
145
- context.literal(part.value);
146
- return;
147
- }
148
- if (isColumnReference(part)) {
149
- context.renderColumnReference(part.columnName);
150
- return;
151
- }
152
- if (isUnsafeSchemaSql(part)) {
153
- if (part.schemaSqlDialect !== context.dialect.name) throw new SchemaExpressionError("dialect-mismatch", `Schema SQL is tagged for "${part.schemaSqlDialect}" but rendered for "${context.dialect.name}"`, mode);
154
- context.append(part.schemaSql);
155
- return;
156
- }
157
- if (!isSchemaExpression(part)) throw new SchemaExpressionError("not-deterministic", "Schema expressions may only compose branded expressions, columns, and literals", mode);
158
- assertSupportedExpression(part, mode);
159
- part.render(context);
160
- }
161
- function assertSupportedExpression(expression, mode) {
162
- if (expression.expressionKind === "subquery" || expression.expressionCategory) throw new SchemaExpressionError("unsupported-expression", "Aggregates, windows, and subqueries are not valid schema expressions", mode);
163
- }
164
- function renderSchemaLiteral(dialect, value, mode) {
165
- if (dialect.renderSchemaLiteral) {
166
- const rendered = dialect.renderSchemaLiteral(value);
167
- if (typeof rendered !== "string" || rendered.includes("?")) throw new SchemaExpressionError("invalid-literal", "A schema literal renderer must return parameter-free SQL text", mode);
168
- return rendered;
169
- }
170
- if (value === null) return "NULL";
171
- if (typeof value === "boolean") return value ? "TRUE" : "FALSE";
172
- if (typeof value === "string") return `'${value.replaceAll("'", "''")}'`;
173
- if (typeof value === "bigint") return String(value);
174
- if (typeof value === "number") {
175
- if (!Number.isFinite(value)) throw new SchemaExpressionError("unsupported-value", "Schema literals require finite numbers", mode);
176
- return Object.is(value, -0) ? "0" : String(value);
177
- }
178
- throw new SchemaExpressionError("unsupported-value", `Unsupported schema literal type: ${value === void 0 ? "undefined" : typeof value}`, mode);
179
- }
180
- //#endregion
181
- //#region src/schema/constraints.ts
182
- /**
183
- * Validate one constraint against a schema adapter dialect. This is kept separate from construction
184
- * because the same declaration can be inspected for more than one target dialect before a
185
- * serializer is selected.
186
- */
187
- function validateConstraintDialect(constraint, dialect, path = ["constraint"]) {
188
- const diagnostics = [];
189
- const extension = constraint.dialect;
190
- if (extension !== void 0) {
191
- const mismatch = dialectMismatchDiagnostic(extension, dialect, [...path, "dialect"]);
192
- if (mismatch !== void 0) diagnostics.push(mismatch);
193
- if (extension.dialect === "postgresql" && "notValid" in extension && extension.notValid === true && (constraint.kind === "primary-key" || constraint.kind === "unique" || constraint.kind === "unique-constraint")) diagnostics.push({
194
- code: "unsupported-dialect-option",
195
- message: "PostgreSQL NOT VALID is not supported for key constraints",
196
- path: [
197
- ...path,
198
- "dialect",
199
- "notValid"
200
- ],
201
- dialect
202
- });
203
- if (extension.dialect === "sqlite" && "onConflict" in extension && extension.onConflict !== void 0 && constraint.kind === "foreign-key") diagnostics.push({
204
- code: "unsupported-dialect-option",
205
- message: "SQLite conflict policies do not apply to foreign keys",
206
- path: [
207
- ...path,
208
- "dialect",
209
- "onConflict"
210
- ],
211
- dialect
212
- });
213
- }
214
- if (constraint.kind === "foreign-key" && constraint.match === "partial" && dialect === "mysql") diagnostics.push({
215
- code: "unsupported-dialect-option",
216
- message: "MySQL does not support MATCH PARTIAL foreign keys",
217
- path: [...path, "match"],
218
- dialect
219
- });
220
- if (constraint.kind === "foreign-key" && constraint.deferrable === true && dialect === "mysql") diagnostics.push({
221
- code: "unsupported-dialect-option",
222
- message: "MySQL foreign keys cannot be declared DEFERRABLE",
223
- path: [...path, "deferrable"],
224
- dialect
225
- });
226
- if (constraint.initially !== void 0 && constraint.deferrable !== true) diagnostics.push({
227
- code: "unsupported-dialect-option",
228
- message: "An initial constraint timing requires deferrable: true",
229
- path: [...path, "initially"],
230
- dialect
231
- });
232
- return Object.freeze(diagnostics);
233
- }
234
- function primaryKey(...columnsAndOptions) {
235
- const last = columnsAndOptions.at(-1);
236
- const options = last && typeof last === "object" && !("expressionKind" in last) ? last : void 0;
237
- return freezeConstraint("primary-key", options ? columnsAndOptions.slice(0, -1) : columnsAndOptions, options);
238
- }
239
- function unique(...columnsAndOptions) {
240
- const last = columnsAndOptions.at(-1);
241
- const options = last && typeof last === "object" && !("expressionKind" in last) ? last : void 0;
242
- return freezeConstraint("unique", options ? columnsAndOptions.slice(0, -1) : columnsAndOptions, options);
243
- }
244
- function uniqueConstraint(...columnsAndOptions) {
245
- const last = columnsAndOptions.at(-1);
246
- const options = last && typeof last === "object" && !("expressionKind" in last) ? last : void 0;
247
- const columns = options ? columnsAndOptions.slice(0, -1) : columnsAndOptions;
248
- const resolvedOptions = options ?? {};
249
- return freezeConstraint("unique-constraint", columns, resolvedOptions, resolvedOptions.nulls ?? "distinct");
250
- }
251
- /** Pair a table with the exact columns targeted by a foreign key. */
252
- function references(table, ...columns) {
253
- return Object.freeze({
254
- table,
255
- columns: Object.freeze([...columns])
256
- });
257
- }
258
- /** Declare a single-column or composite foreign key. */
259
- function foreignKey(columns, target, options) {
260
- return freezeConstraint("foreign-key", columns, options, target);
261
- }
262
- /**
263
- * Reconstruct a foreign key proved by database catalog metadata.
264
- *
265
- * @remarks
266
- * Use this helper for generated or introspection-owned declarations when one or both native SQL
267
- * domains are unresolved. It accepts unresolved domains, but still rejects known domain
268
- * mismatches, unequal tuple arity, and local columns from different sources. The enclosing
269
- * `table()` declaration continues to verify target-column ownership and candidate-key metadata.
270
- * Resolved targets also receive runtime shape, ownership, and arity checks. The returned value
271
- * serializes as an ordinary foreign-key constraint.
272
- */
273
- function catalogForeignKey(columns, target, options) {
274
- assertCatalogForeignKeyColumns(columns, "local");
275
- return freezeConstraint("foreign-key", columns, options, validateCatalogForeignKeyTargetInput(target, columns.length));
276
- }
277
- /** Declare a boolean table invariant. */
278
- function check(expression, options) {
279
- return freezeConstraint("check", void 0, options, expression);
280
- }
281
- /**
282
- * Reconstruct a boolean check from opaque SQL recovered from a database catalog.
283
- *
284
- * @remarks
285
- * This helper records catalog origin as a narrow type-level proof. It gives the opaque expression
286
- * the {@link SqlBoolean} semantic type without changing the ordinary {@link check} contract. Qubu
287
- * does not parse or infer dependencies from the SQL. It preserves the text apart from normalizing
288
- * line endings and retains the dialect tag in serialized schema metadata.
289
- */
290
- function catalogCheck(input, options) {
291
- assertCatalogCheckSql(input);
292
- return freezeConstraint("check", void 0, options, unsafeSchemaSql(input.dialect, input.sql));
293
- }
294
- function assertCatalogCheckSql(value) {
295
- if (typeof value !== "object" || value === null) throw new TypeError("catalogCheck() requires dialect-tagged SQL data");
296
- const input = value;
297
- if (input.dialect !== "postgresql" && input.dialect !== "sqlite" && input.dialect !== "mysql") throw new TypeError(`catalogCheck() requires a supported catalog dialect, received "${String(input.dialect)}"`);
298
- if (typeof input.sql !== "string" || input.sql.trim().length === 0) throw new TypeError("catalogCheck() requires non-empty SQL text");
299
- }
300
- function assertCatalogForeignKeyColumns(value, side) {
301
- if (!Array.isArray(value) || value.length === 0) throw new TypeError(`catalogForeignKey() requires at least one ${side} column`);
302
- if (!value.every(isColumnReference)) throw new TypeError(`catalogForeignKey() requires ${side} columns to be column references`);
303
- }
304
- function validateCatalogForeignKeyTargetInput(value, localArity) {
305
- return typeof value === "function" ? () => validateCatalogForeignKeyTarget(value(), localArity) : validateCatalogForeignKeyTarget(value, localArity);
306
- }
307
- function validateCatalogForeignKeyTarget(value, localArity) {
308
- if (typeof value !== "object" || value === null) throw new TypeError("catalogForeignKey() requires a foreign-key target");
309
- const target = value;
310
- assertCatalogForeignKeyColumns(target.columns, "target");
311
- if (target.columns.length !== localArity) throw new TypeError(`catalogForeignKey() column arity differs: ${localArity} local and ${target.columns.length} target`);
312
- const table = target.table;
313
- if (typeof table !== "object" || table === null || typeof table.columns !== "object" || table.columns === null) throw new TypeError("catalogForeignKey() target must reference a table");
314
- for (const column of target.columns) if (table.columns[column.fieldName] !== column) throw new TypeError(`catalogForeignKey() target column "${column.fieldName}" does not belong to its table`);
315
- return target;
316
- }
317
- function freezeConstraint(kind, columns, options, extra) {
318
- const value = { kind };
319
- const optionValues = options;
320
- if (columns !== void 0) value.columns = Object.freeze([...columns]);
321
- if (kind === "check") value.expression = extra;
322
- else if (kind === "foreign-key") value.target = extra;
323
- else if (kind === "unique-constraint") value.nulls = extra;
324
- if (optionValues?.physicalName !== void 0) value.physicalName = optionValues.physicalName;
325
- if (optionValues?.dialect !== void 0) value.dialect = freezeSchemaMetadata(optionValues.dialect);
326
- if (optionValues?.deferrable !== void 0) value.deferrable = optionValues.deferrable;
327
- if (optionValues?.initially !== void 0) value.initially = optionValues.initially;
328
- if (kind === "foreign-key") {
329
- if (optionValues?.onUpdate !== void 0) value.onUpdate = optionValues.onUpdate;
330
- if (optionValues?.onDelete !== void 0) value.onDelete = optionValues.onDelete;
331
- if (optionValues?.match !== void 0) value.match = optionValues.match;
332
- }
333
- return Object.freeze(value);
334
- }
335
- //#endregion
336
- //#region src/schema/registry.ts
337
- /** The first naming-policy version used by schema metadata. */
338
- const schemaNamingPolicyVersion = 1;
339
- /**
340
- * The built-in naming policy for schema-generated physical names.
341
- *
342
- * Explicit names supplied to `table()` remain unchanged. The policy is used by tooling when it
343
- * needs a physical name for a logical table ID.
344
- */
345
- const defaultSchemaNamingPolicy = Object.freeze({
346
- version: 1,
347
- tableName: snakeCaseIdentifier
348
- });
349
- /** Error thrown when a root schema fails registry or naming validation. */
350
- var SchemaValidationError = class extends Error {
351
- name = "SchemaValidationError";
352
- diagnostics;
353
- /** Alias matching validation libraries that call findings "issues". */
354
- issues;
355
- constructor(diagnostics) {
356
- const frozenDiagnostics = Object.freeze(diagnostics.map((diagnostic) => Object.freeze({
357
- ...diagnostic,
358
- path: Object.freeze([...diagnostic.path]),
359
- relatedPaths: diagnostic.relatedPaths ? Object.freeze(diagnostic.relatedPaths.map((path) => Object.freeze([...path]))) : void 0
360
- })));
361
- super(frozenDiagnostics.map((diagnostic) => diagnostic.message).join("\n"));
362
- this.diagnostics = frozenDiagnostics;
363
- this.issues = frozenDiagnostics;
364
- }
365
- };
366
- function entriesOf(input) {
367
- if (Array.isArray(input)) return input;
368
- return Object.entries(input);
369
- }
370
- function validLogicalId(id) {
371
- return id.length > 0 && id === id.trim() && !/[.\\/\u0000-\u001f\u007f]/u.test(id);
372
- }
373
- function validNamespace(namespace) {
374
- return namespace.length > 0 && namespace === namespace.trim() && !/[.\\/\u0000-\u001f\u007f"']/u.test(namespace);
375
- }
376
- function validateEntries(entries, namespace, namingPolicy) {
377
- const diagnostics = [];
378
- const ids = /* @__PURE__ */ new Map();
379
- const physicalNames = /* @__PURE__ */ new Map();
380
- const generatedNames = /* @__PURE__ */ new Map();
381
- for (const [index, [id, table]] of entries.entries()) {
382
- const path = ["tables", id];
383
- const previousId = ids.get(id);
384
- if (previousId !== void 0) diagnostics.push({
385
- code: "duplicate-table-id",
386
- message: `Table ID "${id}" is declared more than once`,
387
- path,
388
- relatedPaths: [["tables", entries[previousId][0]]]
389
- });
390
- else ids.set(id, index);
391
- if (!validLogicalId(id)) diagnostics.push({
392
- code: "invalid-table-id",
393
- message: `Table ID "${id}" must be a non-empty logical identifier`,
394
- path
395
- });
396
- const physicalName = table.tableName || namingPolicy.tableName(id);
397
- const previousPhysicalName = physicalNames.get(physicalName);
398
- if (previousPhysicalName !== void 0) diagnostics.push({
399
- code: "duplicate-physical-name",
400
- message: `Tables "${entries[previousPhysicalName][0]}" and "${id}" both use physical name "${physicalName}"`,
401
- path: [...path, "physicalName"],
402
- relatedPaths: [[
403
- "tables",
404
- entries[previousPhysicalName][0],
405
- "physicalName"
406
- ]]
407
- });
408
- else physicalNames.set(physicalName, index);
409
- const generatedName = namingPolicy.tableName(id);
410
- const previousGeneratedName = generatedNames.get(generatedName);
411
- if (previousGeneratedName !== void 0) diagnostics.push({
412
- code: "generated-name-collision",
413
- message: `Logical table IDs "${entries[previousGeneratedName][0]}" and "${id}" generate the same physical name "${generatedName}"`,
414
- path: [...path, "generatedName"],
415
- relatedPaths: [[
416
- "tables",
417
- entries[previousGeneratedName][0],
418
- "generatedName"
419
- ]]
420
- });
421
- else generatedNames.set(generatedName, index);
422
- }
423
- if (namespace !== void 0 && !validNamespace(namespace)) diagnostics.push({
424
- code: "invalid-namespace",
425
- message: `Schema namespace "${namespace}" must be a non-empty identifier without qualification or control characters`,
426
- path: ["namespace"]
427
- });
428
- return Object.freeze(diagnostics);
429
- }
430
- function freezeTableNames(entries, namingPolicy) {
431
- return Object.freeze(Object.fromEntries(entries.map(([id, table]) => [id, table.tableName || namingPolicy.tableName(id)])));
432
- }
433
- function createSchema(entries, tables, options = {}) {
434
- const namingPolicy = options.namingPolicy ?? defaultSchemaNamingPolicy;
435
- const diagnostics = validateEntries(entries, options.namespace, namingPolicy);
436
- if (diagnostics.length > 0) throw new SchemaValidationError(diagnostics);
437
- const tableNames = freezeTableNames(entries, namingPolicy);
438
- const registry = Object.freeze(Object.fromEntries(entries.map(([id, table]) => [id, Object.freeze({
439
- id,
440
- table,
441
- physicalName: tableNames[id]
442
- })])));
443
- return Object.freeze({
444
- schemaKind: "schema",
445
- tables: Object.freeze({ ...tables }),
446
- registry,
447
- tableNames,
448
- namespace: options.namespace,
449
- namingPolicy: Object.freeze({ ...namingPolicy })
450
- });
451
- }
452
- function schema(tables, options) {
453
- const entries = entriesOf(tables);
454
- return createSchema(entries, Object.fromEntries(entries), options);
455
- }
456
- /** Generate a v1 physical name for a logical table ID. */
457
- function generatedTableName(logicalId, namingPolicy = defaultSchemaNamingPolicy) {
458
- return namingPolicy.tableName(logicalId);
459
- }
460
- //#endregion
461
- export { index as C, unsafeSchemaSql as S, isUnsafeSchemaSql as _, schemaNamingPolicyVersion as a, renderSchemaSql as b, check as c, references as d, unique as f, defineSchemaExpression as g, SchemaExpressionError as h, schema as i, foreignKey as l, validateConstraintDialect as m, defaultSchemaNamingPolicy as n, catalogCheck as o, uniqueConstraint as p, generatedTableName as r, catalogForeignKey as s, SchemaValidationError as t, primaryKey as u, normalizeSchemaSql as v, validateIndexDialect as w, schemaExpression as x, renderSchemaExpression as y };