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.
- package/README.md +112 -0
- package/dist/{complete-DP7pliuY.mjs → canonical-B5_ouh-c.mjs} +466 -241
- package/dist/codegen.d.mts +2 -2
- package/dist/codegen.mjs +180 -90
- package/dist/column-Cyc2CMnG.mjs +116 -0
- package/dist/column-DDRvD7SF.mjs +721 -0
- package/dist/{constraints-YGyNPQ_z.mjs → constraints-CAmi18Uk.mjs} +8 -3
- package/dist/core.d.mts +3 -3
- package/dist/core.mjs +4 -5
- package/dist/diagnostics-I9vVtXkc.mjs +40 -0
- package/dist/diff.d.mts +9 -9
- package/dist/diff.mjs +152 -102
- package/dist/{explain-CkIK13L_.mjs → explain-BIqEmZha.mjs} +1 -1
- package/dist/expressions-_6JF_J77.mjs +130 -0
- package/dist/index-CaxrMD1A.d.mts +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +414 -77
- package/dist/introspection/mysql.d.mts +26 -0
- package/dist/introspection/mysql.mjs +1278 -0
- package/dist/introspection/postgres.d.mts +43 -0
- package/dist/introspection/postgres.mjs +1889 -0
- package/dist/introspection/sqlite.d.mts +15 -0
- package/dist/introspection/sqlite.mjs +1172 -0
- package/dist/introspection.d.mts +26 -87
- package/dist/introspection.mjs +3 -4351
- package/dist/mysql.d.mts +3 -3
- package/dist/mysql.mjs +8 -7
- package/dist/{errors-Dxv73YJu.mjs → omit-VEr9Ydux.mjs} +5 -1
- package/dist/{on-conflict-DZQ85f1t.mjs → on-conflict-B2rFyHGF.mjs} +78 -8
- package/dist/on-duplicate-key-update-Czsw1Yq-.mjs +95 -0
- package/dist/postgres-hFhd0I9n.mjs +68 -0
- package/dist/postgres.d.mts +3 -3
- package/dist/postgres.mjs +3 -52
- package/dist/registry-BXE_4M9P.mjs +127 -0
- package/dist/{relational-CxnLCqZQ.mjs → relational-CoPBETjI.mjs} +3 -3
- package/dist/schema.d.mts +2 -2
- package/dist/schema.mjs +8 -7
- package/dist/{serialize-BN07IK0v.mjs → serialize-CyobNEx-.mjs} +174 -30
- package/dist/serialize-Du2UPZMt.d.mts +92 -0
- package/dist/snapshot/mysql.d.mts +5 -5
- package/dist/snapshot/mysql.mjs +32 -32
- package/dist/snapshot/postgres.d.mts +5 -5
- package/dist/snapshot/postgres.mjs +25 -25
- package/dist/snapshot/sqlite.d.mts +5 -5
- package/dist/snapshot/sqlite.mjs +30 -30
- package/dist/snapshot-mIb-Zzb5.mjs +1489 -0
- package/dist/snapshot.d.mts +4 -5
- package/dist/snapshot.mjs +3 -4
- package/dist/{source-SqrKWjFJ.mjs → source-DYSUqzvb.mjs} +2 -2
- package/dist/sqlite.d.mts +2 -2
- package/dist/sqlite.mjs +10 -10
- package/dist/{table-BwflqeAj.mjs → table-B8zEq0az.mjs} +4 -4
- package/dist/{types-BIJsj2fJ.mjs → types-CYHpSPwj.mjs} +10 -7
- package/dist/{types-CTCqtFlS.d.mts → types-CiMvKi5V.d.mts} +14 -4
- package/dist/{types-C0VkiwpR.d.mts → types-Dqr4o2I1.d.mts} +640 -189
- package/dist/value-CpaUFtjw.mjs +45 -0
- package/dist/vite/ambient.d.ts +2 -0
- package/dist/vite.d.mts +1 -1
- package/dist/vite.mjs +2 -0
- package/docs/dialects-and-execution.md +61 -11
- package/docs/getting-started.md +1 -1
- package/docs/guides/compose-queries.md +22 -0
- package/docs/guides/drizzle.md +11 -11
- package/docs/guides/extensions/typed-expressions.md +5 -3
- package/docs/guides/json.md +95 -2
- package/docs/guides/mutations.md +125 -0
- package/docs/guides/valtio-sync.md +113 -0
- package/docs/index.md +1 -1
- package/docs/migrations/adapters.md +58 -21
- package/docs/migrations/index.md +50 -12
- package/docs/migrations/operations.md +20 -6
- package/docs/migrations/recovery.md +7 -1
- package/docs/query-model/fragments.md +21 -1
- package/docs/reference/introspection-support.md +16 -18
- package/docs/reference/supported-surface.md +23 -11
- package/docs/schema/catalog-model.md +20 -7
- package/docs/schema/code-generation.md +4 -3
- package/docs/schema/diff.md +2 -2
- package/docs/schema/introspection.md +24 -16
- package/docs/schema/snapshots.md +18 -8
- package/docs/schema/storage-and-schema-sql.md +7 -2
- package/docs/sql-semantic-types.md +8 -0
- package/package.json +14 -1
- package/dist/canonical-DMvR9yBe.mjs +0 -972
- package/dist/column-Da37jYSD.mjs +0 -309
- package/dist/column-r1Y4ivwt.mjs +0 -327
- package/dist/complete-types-CY0KbzNw.d.mts +0 -371
- package/dist/index-B2rZf3-2.d.mts +0 -32
- package/dist/json-Db7XRD91.mjs +0 -169
- package/dist/omit-OxV58AwX.mjs +0 -5
- package/dist/registry-BRcUuazJ.mjs +0 -256
- package/dist/serialize-CEIIlWhC.d.mts +0 -66
- package/dist/snapshot-Xam8-q0j.mjs +0 -392
- package/dist/standard-DfcZEVOj.mjs +0 -12
- package/dist/value-D14I_XgL.mjs +0 -29
|
@@ -0,0 +1,721 @@
|
|
|
1
|
+
//#region src/core/dialect.ts
|
|
2
|
+
const standardCastTypes = {
|
|
3
|
+
integer: "INTEGER",
|
|
4
|
+
decimal: "DECIMAL",
|
|
5
|
+
text: "TEXT",
|
|
6
|
+
boolean: "BOOLEAN",
|
|
7
|
+
date: "DATE",
|
|
8
|
+
timestamp: "TIMESTAMP",
|
|
9
|
+
uuid: "UUID",
|
|
10
|
+
json: "JSON",
|
|
11
|
+
bigint: "BIGINT",
|
|
12
|
+
binary: "VARBINARY"
|
|
13
|
+
};
|
|
14
|
+
/** Resolve a logical or explicitly named CAST target for a dialect. */
|
|
15
|
+
function resolveCastTarget(dialect, target) {
|
|
16
|
+
if (target.kind === "named-cast") return target.typeName;
|
|
17
|
+
return dialect.castTypes?.[target.type] ?? standardCastTypes[target.type];
|
|
18
|
+
}
|
|
19
|
+
const quoteIdentifier = (identifier) => `"${identifier.replaceAll("\"", "\"\"")}"`;
|
|
20
|
+
function createDialect(options) {
|
|
21
|
+
return Object.freeze({
|
|
22
|
+
name: options.name,
|
|
23
|
+
quoteIdentifier: options.quoteIdentifier ?? quoteIdentifier,
|
|
24
|
+
placeholder: options.placeholder,
|
|
25
|
+
pagination: options.pagination,
|
|
26
|
+
rowLocking: options.rowLocking,
|
|
27
|
+
json: options.json,
|
|
28
|
+
castTypes: options.castTypes ? Object.freeze({ ...options.castTypes }) : void 0,
|
|
29
|
+
renderSchemaLiteral: options.renderSchemaLiteral,
|
|
30
|
+
explain: options.explain,
|
|
31
|
+
capabilities: Object.freeze([...options.capabilities ?? [], ...options.json ? ["json"] : []])
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Check a capability at runtime for callers that intentionally bypass the typed render boundary or
|
|
36
|
+
* use a dialect supplied by an older integration.
|
|
37
|
+
*/
|
|
38
|
+
function assertDialectCapability(dialect, capability) {
|
|
39
|
+
if (dialect.capabilities?.includes(capability)) return;
|
|
40
|
+
throw new Error(`Dialect "${dialect.name}" does not support the "${capability}" capability`);
|
|
41
|
+
}
|
|
42
|
+
/** Extend a dialect with capabilities owned by an addon without changing its SQL policies. */
|
|
43
|
+
function extendDialect(dialect, additionalCapabilities) {
|
|
44
|
+
const capabilities = Object.freeze([.../* @__PURE__ */ new Set([...dialect.capabilities ?? [], ...additionalCapabilities])]);
|
|
45
|
+
return Object.freeze({
|
|
46
|
+
...dialect,
|
|
47
|
+
capabilities
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region src/dialects/json.ts
|
|
52
|
+
function jsonPath(path) {
|
|
53
|
+
return path.reduce((result, segment) => typeof segment === "number" ? `${result}[${segment}]` : `${result}.${JSON.stringify(segment)}`, "$");
|
|
54
|
+
}
|
|
55
|
+
function appendPath(context, path, requiredType) {
|
|
56
|
+
context.append("'");
|
|
57
|
+
context.append(jsonPath(path).replaceAll("'", "''"));
|
|
58
|
+
if (requiredType) {
|
|
59
|
+
const type = requiredType === "text" ? "string" : requiredType;
|
|
60
|
+
context.append(` ? (@.type() == "${type}")`);
|
|
61
|
+
}
|
|
62
|
+
context.append("'");
|
|
63
|
+
}
|
|
64
|
+
function renderSqlJsonValue(context, document, path, returning, requiredType) {
|
|
65
|
+
context.append("JSON_VALUE(");
|
|
66
|
+
context.render(document);
|
|
67
|
+
context.append(", ");
|
|
68
|
+
appendPath(context, path, requiredType);
|
|
69
|
+
context.append(` RETURNING ${returning} NULL ON EMPTY NULL ON ERROR)`);
|
|
70
|
+
}
|
|
71
|
+
const standardJson = {
|
|
72
|
+
renderScalar(context, document, path, kind) {
|
|
73
|
+
renderSqlJsonValue(context, document, path, kind === "text" ? "VARCHAR(512)" : kind === "number" ? "DOUBLE PRECISION" : "BOOLEAN", kind);
|
|
74
|
+
},
|
|
75
|
+
renderExists(context, document, path) {
|
|
76
|
+
context.append("COALESCE(JSON_EXISTS(");
|
|
77
|
+
context.render(document);
|
|
78
|
+
context.append(", ");
|
|
79
|
+
appendPath(context, path);
|
|
80
|
+
context.append(" FALSE ON ERROR), FALSE)");
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
function renderPostgresTarget(context, document, path) {
|
|
84
|
+
context.append("jsonb_path_query_first(CAST(");
|
|
85
|
+
context.render(document);
|
|
86
|
+
context.append(" AS JSONB), ");
|
|
87
|
+
appendPath(context, path);
|
|
88
|
+
context.append(")");
|
|
89
|
+
}
|
|
90
|
+
function postgresType(kind) {
|
|
91
|
+
return kind === "text" ? "string" : kind;
|
|
92
|
+
}
|
|
93
|
+
const postgresJson = {
|
|
94
|
+
renderScalar(context, document, path, kind) {
|
|
95
|
+
context.append("(CASE jsonb_typeof(");
|
|
96
|
+
renderPostgresTarget(context, document, path);
|
|
97
|
+
context.append(`) WHEN '${postgresType(kind)}' THEN `);
|
|
98
|
+
if (kind !== "text") context.append("CAST(");
|
|
99
|
+
context.append("(");
|
|
100
|
+
renderPostgresTarget(context, document, path);
|
|
101
|
+
context.append(" #>> '{}')");
|
|
102
|
+
if (kind !== "text") context.append(kind === "number" ? " AS DOUBLE PRECISION)" : " AS BOOLEAN)");
|
|
103
|
+
context.append(" END)");
|
|
104
|
+
},
|
|
105
|
+
renderExists(context, document, path) {
|
|
106
|
+
context.append("COALESCE(jsonb_path_exists(CAST(");
|
|
107
|
+
context.render(document);
|
|
108
|
+
context.append(" AS JSONB), ");
|
|
109
|
+
appendPath(context, path);
|
|
110
|
+
context.append(", '{}'::JSONB, TRUE), FALSE)");
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
function renderMySqlExtract(context, document, path) {
|
|
114
|
+
context.append("JSON_EXTRACT(");
|
|
115
|
+
context.render(document);
|
|
116
|
+
context.append(", ");
|
|
117
|
+
appendPath(context, path);
|
|
118
|
+
context.append(")");
|
|
119
|
+
}
|
|
120
|
+
function mysqlType(kind) {
|
|
121
|
+
return kind === "text" ? "STRING" : kind === "number" ? "INTEGER" : "BOOLEAN";
|
|
122
|
+
}
|
|
123
|
+
const mysqlJson = {
|
|
124
|
+
renderScalar(context, document, path, kind) {
|
|
125
|
+
context.append("(CASE JSON_TYPE(");
|
|
126
|
+
renderMySqlExtract(context, document, path);
|
|
127
|
+
context.append(`) WHEN '${mysqlType(kind)}' THEN `);
|
|
128
|
+
if (kind === "boolean") context.append("(");
|
|
129
|
+
renderSqlJsonValue(context, document, path, kind === "text" ? "CHAR" : kind === "number" ? "DOUBLE" : "CHAR");
|
|
130
|
+
if (kind === "boolean") context.append(" = 'true')");
|
|
131
|
+
context.append(" END)");
|
|
132
|
+
},
|
|
133
|
+
renderExists(context, document, path) {
|
|
134
|
+
context.append("COALESCE(JSON_CONTAINS_PATH(");
|
|
135
|
+
context.render(document);
|
|
136
|
+
context.append(", 'one', ");
|
|
137
|
+
appendPath(context, path);
|
|
138
|
+
context.append("), FALSE)");
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
function renderSqliteExtract(context, document, path) {
|
|
142
|
+
context.append("json_extract(");
|
|
143
|
+
context.render(document);
|
|
144
|
+
context.append(", ");
|
|
145
|
+
appendPath(context, path);
|
|
146
|
+
context.append(")");
|
|
147
|
+
}
|
|
148
|
+
function renderSqliteType(context, document, path) {
|
|
149
|
+
context.append("json_type(");
|
|
150
|
+
context.render(document);
|
|
151
|
+
context.append(", ");
|
|
152
|
+
appendPath(context, path);
|
|
153
|
+
context.append(")");
|
|
154
|
+
}
|
|
155
|
+
const sqliteJson = {
|
|
156
|
+
renderScalar(context, document, path, kind) {
|
|
157
|
+
context.append("(CASE ");
|
|
158
|
+
renderSqliteType(context, document, path);
|
|
159
|
+
if (kind === "text") {
|
|
160
|
+
context.append(" WHEN 'text' THEN ");
|
|
161
|
+
renderSqliteExtract(context, document, path);
|
|
162
|
+
} else if (kind === "number") {
|
|
163
|
+
context.append(" WHEN 'integer' THEN ");
|
|
164
|
+
renderSqliteExtract(context, document, path);
|
|
165
|
+
context.append(" WHEN 'real' THEN ");
|
|
166
|
+
renderSqliteExtract(context, document, path);
|
|
167
|
+
} else context.append(" WHEN 'true' THEN TRUE WHEN 'false' THEN FALSE");
|
|
168
|
+
context.append(" END)");
|
|
169
|
+
},
|
|
170
|
+
renderExists(context, document, path) {
|
|
171
|
+
context.append("(");
|
|
172
|
+
renderSqliteType(context, document, path);
|
|
173
|
+
context.append(" IS NOT NULL)");
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
//#endregion
|
|
177
|
+
//#region src/result.ts
|
|
178
|
+
const resultValueMetadata = Symbol("qubu.result-value-metadata");
|
|
179
|
+
function resultValue(type, decoder, sqlType) {
|
|
180
|
+
return type === void 0 && decoder === void 0 && sqlType === void 0 ? void 0 : Object.freeze({
|
|
181
|
+
...type === void 0 ? {} : { type },
|
|
182
|
+
...decoder === void 0 ? {} : { decoder },
|
|
183
|
+
...sqlType === void 0 ? {} : { sqlType }
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
function resultValueOf(value) {
|
|
187
|
+
if (typeof value !== "object" || value === null) return;
|
|
188
|
+
return value[resultValueMetadata];
|
|
189
|
+
}
|
|
190
|
+
function attachResultValue(value, metadata) {
|
|
191
|
+
if (metadata === void 0) return value;
|
|
192
|
+
return Object.freeze({
|
|
193
|
+
...value,
|
|
194
|
+
[resultValueMetadata]: metadata
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
function createResultShape(fields) {
|
|
198
|
+
return Object.freeze({ fields: Object.freeze(fields.map((field) => Object.freeze({ ...field }))) });
|
|
199
|
+
}
|
|
200
|
+
function resultShapeValue(shape, name) {
|
|
201
|
+
const field = shape.fields.find((candidate) => candidate.name === name);
|
|
202
|
+
return field === void 0 ? void 0 : resultValue(field.type, field.decoder, field.sqlType);
|
|
203
|
+
}
|
|
204
|
+
/** Decode SQLite/MySQL-style boolean values while accepting native booleans. */
|
|
205
|
+
const booleanResultDecoder = (value) => {
|
|
206
|
+
if (typeof value === "boolean") return value;
|
|
207
|
+
if (value === 0 || value === 0n) return false;
|
|
208
|
+
if (value === 1 || value === 1n) return true;
|
|
209
|
+
throw new TypeError("Expected a boolean or numeric 0/1 result");
|
|
210
|
+
};
|
|
211
|
+
/** Decode a SQL DATE string as a JavaScript Date at UTC midnight. */
|
|
212
|
+
const dateResultDecoder = (value) => {
|
|
213
|
+
if (value instanceof Date && !Number.isNaN(value.getTime())) return value;
|
|
214
|
+
if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}$/u.test(value)) throw new TypeError("Expected a valid Date or YYYY-MM-DD result");
|
|
215
|
+
const decoded = /* @__PURE__ */ new Date(`${value}T00:00:00.000Z`);
|
|
216
|
+
if (Number.isNaN(decoded.getTime()) || decoded.toISOString().slice(0, 10) !== value) throw new TypeError("Expected a valid calendar date result");
|
|
217
|
+
return decoded;
|
|
218
|
+
};
|
|
219
|
+
/** Decode a SQL/ISO timestamp string while accepting driver-created Dates. */
|
|
220
|
+
const timestampResultDecoder = (value) => {
|
|
221
|
+
if (value instanceof Date && !Number.isNaN(value.getTime())) return value;
|
|
222
|
+
if (typeof value !== "string") throw new TypeError("Expected a valid Date or timestamp string result");
|
|
223
|
+
const normalized = value.includes("T") ? value : value.replace(" ", "T");
|
|
224
|
+
const decoded = new Date(/(?:Z|[+-]\d{2}:?\d{2})$/u.test(normalized) ? normalized : `${normalized}Z`);
|
|
225
|
+
if (Number.isNaN(decoded.getTime())) throw new TypeError("Expected a valid timestamp result");
|
|
226
|
+
return decoded;
|
|
227
|
+
};
|
|
228
|
+
/** Decode a serialized JSON result. Use only when the adapter returns JSON text. */
|
|
229
|
+
const jsonTextResultDecoder = (value) => {
|
|
230
|
+
if (typeof value !== "string") return value;
|
|
231
|
+
return JSON.parse(value);
|
|
232
|
+
};
|
|
233
|
+
/** Decode an exact integer result without ever accepting an unsafe JavaScript number. */
|
|
234
|
+
const bigintResultDecoder = (value) => {
|
|
235
|
+
if (typeof value === "bigint") return value;
|
|
236
|
+
if (typeof value === "number") {
|
|
237
|
+
if (Number.isSafeInteger(value)) return BigInt(value);
|
|
238
|
+
throw new TypeError("Expected a bigint, canonical integer string, or safe integer result");
|
|
239
|
+
}
|
|
240
|
+
if (typeof value !== "string" || !/^[+-]?\d+$/u.test(value)) throw new TypeError("Expected a bigint, canonical integer string, or safe integer result");
|
|
241
|
+
return BigInt(value);
|
|
242
|
+
};
|
|
243
|
+
/** Error raised when a projected driver value cannot be decoded. */
|
|
244
|
+
var ResultDecodingError = class extends TypeError {
|
|
245
|
+
name = "ResultDecodingError";
|
|
246
|
+
field;
|
|
247
|
+
rowIndex;
|
|
248
|
+
resultType;
|
|
249
|
+
constructor(field, rowIndex, resultType) {
|
|
250
|
+
super(`Could not decode result field "${field}" in row ${rowIndex}${resultType === void 0 ? "" : ` as ${resultType}`}`);
|
|
251
|
+
this.field = field;
|
|
252
|
+
this.rowIndex = rowIndex;
|
|
253
|
+
this.resultType = resultType;
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
function decodeResultRow(row, shape, decoders, dialect, rowIndex) {
|
|
257
|
+
let decoded;
|
|
258
|
+
for (const field of shape.fields) {
|
|
259
|
+
const decoder = field.decoder ?? (field.type === "bigint" ? decoders?.bigint ?? bigintResultDecoder : field.type ? decoders?.[field.type] : void 0);
|
|
260
|
+
if (decoder === void 0 || row[field.name] === null) continue;
|
|
261
|
+
try {
|
|
262
|
+
decoded ??= { ...row };
|
|
263
|
+
decoded[field.name] = decoder(row[field.name], {
|
|
264
|
+
dialect,
|
|
265
|
+
field: field.name,
|
|
266
|
+
rowIndex
|
|
267
|
+
});
|
|
268
|
+
} catch {
|
|
269
|
+
throw new ResultDecodingError(field.name, rowIndex, field.type);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return decoded ?? row;
|
|
273
|
+
}
|
|
274
|
+
//#endregion
|
|
275
|
+
//#region src/core/fragment.ts
|
|
276
|
+
function fragment(render) {
|
|
277
|
+
return Object.freeze({ render });
|
|
278
|
+
}
|
|
279
|
+
function sequence(parts, separator = " ") {
|
|
280
|
+
return fragment((context) => {
|
|
281
|
+
let first = true;
|
|
282
|
+
for (const part of parts) {
|
|
283
|
+
if (!first) context.append(separator);
|
|
284
|
+
context.render(part);
|
|
285
|
+
first = false;
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
function parenthesize(part) {
|
|
290
|
+
return fragment((context) => {
|
|
291
|
+
context.append("(");
|
|
292
|
+
context.render(part);
|
|
293
|
+
context.append(")");
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
function isFragment(value) {
|
|
297
|
+
return typeof value === "object" && value !== null && "render" in value && typeof value.render === "function";
|
|
298
|
+
}
|
|
299
|
+
//#endregion
|
|
300
|
+
//#region src/expressions/types.ts
|
|
301
|
+
/** Runtime/type-level proof that an expression is safe to use in schema SQL. */
|
|
302
|
+
const schemaExpressionBrand = Symbol("qubu.schema-expression");
|
|
303
|
+
/** Add a concrete dialect requirement without dropping expression metadata. */
|
|
304
|
+
function withDialectCapability(expression, capability) {
|
|
305
|
+
const wrapped = makeExpression(expression.expressionKind, (context) => {
|
|
306
|
+
assertDialectCapability(context.dialect, capability);
|
|
307
|
+
context.render(expression);
|
|
308
|
+
}, expression.expressionCategory, resultValueOf(expression));
|
|
309
|
+
return isSchemaExpression(expression) ? markSchemaExpression(wrapped) : wrapped;
|
|
310
|
+
}
|
|
311
|
+
function makeExpression(expressionKind, render, expressionCategory, result) {
|
|
312
|
+
return attachResultValue(Object.freeze({
|
|
313
|
+
expressionKind,
|
|
314
|
+
...expressionCategory ? { expressionCategory } : {},
|
|
315
|
+
...fragment(render)
|
|
316
|
+
}), result);
|
|
317
|
+
}
|
|
318
|
+
/** Mark a query-only expression category without changing its SQL renderer. */
|
|
319
|
+
function markExpressionCategory(expression, category) {
|
|
320
|
+
return Object.freeze({
|
|
321
|
+
...expression,
|
|
322
|
+
expressionCategory: category
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Mark a built-in or explicitly audited renderer as schema-deterministic. Prefer
|
|
327
|
+
* {@link defineSchemaExpression} for application extensions because it supplies a restricted schema
|
|
328
|
+
* rendering context.
|
|
329
|
+
*/
|
|
330
|
+
function makeSchemaExpression(expressionKind, render, result) {
|
|
331
|
+
const expression = makeExpression(expressionKind, render, void 0, result);
|
|
332
|
+
return Object.freeze({
|
|
333
|
+
...expression,
|
|
334
|
+
[schemaExpressionBrand]: true
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
/** Add the schema-determinism brand to an explicitly audited expression. */
|
|
338
|
+
function markSchemaExpression(expression) {
|
|
339
|
+
if (isSchemaExpression(expression)) return expression;
|
|
340
|
+
return Object.freeze({
|
|
341
|
+
...expression,
|
|
342
|
+
[schemaExpressionBrand]: true
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
/** Test the runtime brand used by schema rendering and validation. */
|
|
346
|
+
function isSchemaExpression(value) {
|
|
347
|
+
return typeof value === "object" && value !== null && schemaExpressionBrand in value && value[schemaExpressionBrand] === true;
|
|
348
|
+
}
|
|
349
|
+
function isExpression(value) {
|
|
350
|
+
return typeof value === "object" && value !== null && "expressionKind" in value && "render" in value && typeof value.render === "function";
|
|
351
|
+
}
|
|
352
|
+
//#endregion
|
|
353
|
+
//#region src/core/naming.ts
|
|
354
|
+
/** Convert an application-facing field name to a SQL-facing identifier. */
|
|
355
|
+
function snakeCaseIdentifier(name) {
|
|
356
|
+
return name.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[-\s]+/g, "_").toLowerCase();
|
|
357
|
+
}
|
|
358
|
+
function resolveSqlNames(fields) {
|
|
359
|
+
const sqlNames = {};
|
|
360
|
+
const fieldsBySqlName = /* @__PURE__ */ new Map();
|
|
361
|
+
for (const field of fields) {
|
|
362
|
+
const sqlName = field.sqlName ?? snakeCaseIdentifier(field.fieldName);
|
|
363
|
+
if (sqlName.length === 0) throw new Error(`SQL name for field "${field.fieldName}" cannot be empty`);
|
|
364
|
+
const existingField = fieldsBySqlName.get(sqlName);
|
|
365
|
+
if (existingField) throw new Error(`Fields "${existingField}" and "${field.fieldName}" both resolve to SQL name "${sqlName}"`);
|
|
366
|
+
fieldsBySqlName.set(sqlName, field.fieldName);
|
|
367
|
+
sqlNames[field.fieldName] = sqlName;
|
|
368
|
+
}
|
|
369
|
+
return Object.freeze(sqlNames);
|
|
370
|
+
}
|
|
371
|
+
//#endregion
|
|
372
|
+
//#region src/schema/metadata.ts
|
|
373
|
+
/** Error raised when relational metadata cannot be represented safely. */
|
|
374
|
+
var SchemaMetadataValidationError = class extends TypeError {
|
|
375
|
+
name = "SchemaMetadataValidationError";
|
|
376
|
+
diagnostics;
|
|
377
|
+
issues;
|
|
378
|
+
constructor(diagnostics) {
|
|
379
|
+
const frozenDiagnostics = Object.freeze(diagnostics.map((diagnostic) => Object.freeze({
|
|
380
|
+
...diagnostic,
|
|
381
|
+
path: Object.freeze([...diagnostic.path]),
|
|
382
|
+
relatedPaths: diagnostic.relatedPaths ? Object.freeze(diagnostic.relatedPaths.map((path) => Object.freeze([...path]))) : void 0
|
|
383
|
+
})));
|
|
384
|
+
super(frozenDiagnostics.map((diagnostic) => diagnostic.message).join("\n"));
|
|
385
|
+
this.diagnostics = frozenDiagnostics;
|
|
386
|
+
this.issues = frozenDiagnostics;
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
/** Generate the version-one physical name for a relational object ID. */
|
|
390
|
+
function generatedSchemaObjectName(id) {
|
|
391
|
+
return snakeCaseIdentifier(id);
|
|
392
|
+
}
|
|
393
|
+
/** Deep-freeze plain metadata values without introducing executable nodes. */
|
|
394
|
+
function freezeSchemaMetadata(value) {
|
|
395
|
+
if (Array.isArray(value)) return Object.freeze(value.map((item) => freezeSchemaMetadata(item)));
|
|
396
|
+
if (typeof value !== "object" || value === null) return value;
|
|
397
|
+
const frozen = Object.fromEntries(Object.entries(value).map(([key, nested]) => [key, freezeSchemaMetadata(nested)]));
|
|
398
|
+
return Object.freeze(frozen);
|
|
399
|
+
}
|
|
400
|
+
/** Check the portable identifier subset used for generated metadata names. */
|
|
401
|
+
function isValidSchemaObjectName(name) {
|
|
402
|
+
return name.length > 0 && name === name.trim() && !/[.\\/\u0000-\u001f\u007f"']/u.test(name);
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Attach a record key and resolved physical name without changing the enumerable shape of legacy
|
|
406
|
+
* constraint/index values. The properties are still ordinary read-only runtime metadata and are
|
|
407
|
+
* available to snapshot traversals.
|
|
408
|
+
*/
|
|
409
|
+
function materializeSchemaObjectIdentity(value, id, physicalName) {
|
|
410
|
+
const materialized = { ...value };
|
|
411
|
+
Object.defineProperties(materialized, {
|
|
412
|
+
id: {
|
|
413
|
+
configurable: false,
|
|
414
|
+
enumerable: false,
|
|
415
|
+
value: id,
|
|
416
|
+
writable: false
|
|
417
|
+
},
|
|
418
|
+
physicalName: {
|
|
419
|
+
configurable: false,
|
|
420
|
+
enumerable: false,
|
|
421
|
+
value: physicalName ?? generatedSchemaObjectName(id),
|
|
422
|
+
writable: false
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
return Object.freeze(materialized);
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Resolve and validate names for one metadata record. The caller decides whether constraint and
|
|
429
|
+
* index names share a database namespace.
|
|
430
|
+
*/
|
|
431
|
+
function materializeSchemaObjectRecord(record, kind) {
|
|
432
|
+
const diagnostics = [];
|
|
433
|
+
const names = /* @__PURE__ */ new Map();
|
|
434
|
+
const result = {};
|
|
435
|
+
for (const [id, value] of Object.entries(record)) {
|
|
436
|
+
const physicalName = value.physicalName ?? generatedSchemaObjectName(id);
|
|
437
|
+
const path = [kind === "constraint" ? "constraints" : "indexes", id];
|
|
438
|
+
if (!isValidSchemaObjectName(physicalName)) diagnostics.push({
|
|
439
|
+
code: "invalid-physical-name",
|
|
440
|
+
message: `The ${kind} "${id}" has invalid physical name "${physicalName}"`,
|
|
441
|
+
path: [...path, "physicalName"]
|
|
442
|
+
});
|
|
443
|
+
const previousId = names.get(physicalName);
|
|
444
|
+
if (previousId !== void 0) diagnostics.push({
|
|
445
|
+
code: "duplicate-physical-name",
|
|
446
|
+
message: `The ${kind}s "${previousId}" and "${id}" both use physical name "${physicalName}"`,
|
|
447
|
+
path: [...path, "physicalName"],
|
|
448
|
+
relatedPaths: [[
|
|
449
|
+
kind === "constraint" ? "constraints" : "indexes",
|
|
450
|
+
previousId,
|
|
451
|
+
"physicalName"
|
|
452
|
+
]]
|
|
453
|
+
});
|
|
454
|
+
else names.set(physicalName, id);
|
|
455
|
+
result[id] = materializeSchemaObjectIdentity(value, id, physicalName);
|
|
456
|
+
}
|
|
457
|
+
if (diagnostics.length > 0) throw new SchemaMetadataValidationError(diagnostics);
|
|
458
|
+
return Object.freeze(result);
|
|
459
|
+
}
|
|
460
|
+
/** Return a diagnostic when a dialect extension is used by another adapter. */
|
|
461
|
+
function dialectMismatchDiagnostic(extension, dialect, path) {
|
|
462
|
+
if (extension.dialect === dialect) return;
|
|
463
|
+
return {
|
|
464
|
+
code: "dialect-mismatch",
|
|
465
|
+
message: `Dialect extension belongs to "${extension.dialect}" but the active schema dialect is "${dialect}"`,
|
|
466
|
+
path,
|
|
467
|
+
dialect
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
/** Throw a structured error for one or more dialect capability findings. */
|
|
471
|
+
function assertSchemaDialectSupport(diagnostics) {
|
|
472
|
+
if (diagnostics.length > 0) throw new SchemaMetadataValidationError(diagnostics);
|
|
473
|
+
}
|
|
474
|
+
//#endregion
|
|
475
|
+
//#region src/schema/column-behavior.ts
|
|
476
|
+
/** A column behavior error with a stable code and optional property path. */
|
|
477
|
+
var ColumnBehaviorError = class extends TypeError {
|
|
478
|
+
code;
|
|
479
|
+
path;
|
|
480
|
+
constructor(code, message, path) {
|
|
481
|
+
super(message);
|
|
482
|
+
this.name = "ColumnBehaviorError";
|
|
483
|
+
this.code = code;
|
|
484
|
+
this.path = path;
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
/** Build a canonical literal node from a supported JavaScript scalar. */
|
|
488
|
+
function canonicalLiteral(value) {
|
|
489
|
+
if (value === null) return Object.freeze({ kind: "null" });
|
|
490
|
+
if (typeof value === "boolean") return Object.freeze({
|
|
491
|
+
kind: "boolean",
|
|
492
|
+
value
|
|
493
|
+
});
|
|
494
|
+
if (typeof value === "string") return Object.freeze({
|
|
495
|
+
kind: "string",
|
|
496
|
+
value
|
|
497
|
+
});
|
|
498
|
+
if (typeof value === "bigint") return Object.freeze({
|
|
499
|
+
kind: "bigint",
|
|
500
|
+
value: String(value)
|
|
501
|
+
});
|
|
502
|
+
if (!Number.isFinite(value)) throw new ColumnBehaviorError("invalid-default", "Default literal numbers must be finite", "default.value");
|
|
503
|
+
return Object.freeze({
|
|
504
|
+
kind: "number",
|
|
505
|
+
value: Object.is(value, -0) ? "0" : String(value)
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
function literalDefault(value) {
|
|
509
|
+
return Object.freeze({
|
|
510
|
+
kind: "literal",
|
|
511
|
+
value: canonicalLiteral(value)
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
/** Normalize a public default input into its canonical snapshot descriptor. */
|
|
515
|
+
function normalizeDefault(value) {
|
|
516
|
+
if (isSchemaExpression(value)) return Object.freeze({
|
|
517
|
+
kind: "expression",
|
|
518
|
+
expression: value
|
|
519
|
+
});
|
|
520
|
+
if (isExternalDefaultDescriptor(value)) return externalDefault();
|
|
521
|
+
return literalDefault(value);
|
|
522
|
+
}
|
|
523
|
+
/** Mark a legacy or externally managed database default explicitly. */
|
|
524
|
+
function externalDefault() {
|
|
525
|
+
return Object.freeze({ kind: "external" });
|
|
526
|
+
}
|
|
527
|
+
/** Create an immutable generated-column descriptor. */
|
|
528
|
+
function generatedColumn(expression, mode) {
|
|
529
|
+
assertSchemaExpression(expression, "generatedColumn.expression", "invalid-generated-column");
|
|
530
|
+
const resolvedMode = typeof mode === "string" ? mode : mode?.mode ?? "stored";
|
|
531
|
+
if (resolvedMode !== "stored" && resolvedMode !== "virtual") throw new ColumnBehaviorError("invalid-generated-column", `Generated-column mode must be "stored" or "virtual", received "${String(resolvedMode)}"`, "generatedColumn.mode");
|
|
532
|
+
return Object.freeze({
|
|
533
|
+
kind: "expression",
|
|
534
|
+
expression,
|
|
535
|
+
mode: resolvedMode
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
/** Mark a legacy or externally managed generated column explicitly. */
|
|
539
|
+
function externalGeneratedColumn() {
|
|
540
|
+
return Object.freeze({ kind: "external" });
|
|
541
|
+
}
|
|
542
|
+
/** Describe a database identity column without inventing a generated SQL expression. */
|
|
543
|
+
function identityColumn(generation, options) {
|
|
544
|
+
const resolvedGeneration = generation ?? "by-default";
|
|
545
|
+
if (resolvedGeneration !== "always" && resolvedGeneration !== "by-default") throw new ColumnBehaviorError("invalid-identity", `Identity generation must be "always" or "by-default", received "${String(resolvedGeneration)}"`, "identity.generation");
|
|
546
|
+
return Object.freeze({
|
|
547
|
+
kind: "identity",
|
|
548
|
+
generation: resolvedGeneration,
|
|
549
|
+
...options?.options === void 0 ? {} : { options: freezeIdentityOptions(options.options) },
|
|
550
|
+
...options?.dialect === void 0 ? {} : { dialect: freezeSchemaMetadata(options.dialect) }
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
/** Normalize complete and legacy column behavior into immutable metadata. */
|
|
554
|
+
function resolveColumnBehavior(options) {
|
|
555
|
+
const hasDefaultFlag = options.hasDefault === true;
|
|
556
|
+
const defaultFn = options.defaultFn;
|
|
557
|
+
const generatedFlag = options.generated === true;
|
|
558
|
+
const defaultDescriptor = options.default === void 0 ? void 0 : normalizeDefault(options.default);
|
|
559
|
+
const generatedDescriptor = options.generatedColumn;
|
|
560
|
+
const identityDescriptor = options.identity;
|
|
561
|
+
const onUpdateExpression = options.onUpdate;
|
|
562
|
+
if (defaultFn !== void 0 && typeof defaultFn !== "function") throw new ColumnBehaviorError("invalid-runtime-default", "Column defaultFn must be a function", "defaultFn");
|
|
563
|
+
if (defaultFn !== void 0 && (generatedFlag || generatedDescriptor !== void 0 || identityDescriptor !== void 0)) throw new ColumnBehaviorError("runtime-default-generated-conflict", "A runtime default cannot be combined with generated-column or identity metadata", "defaultFn");
|
|
564
|
+
if (onUpdateExpression !== void 0 && !isSchemaExpression(onUpdateExpression)) throw new ColumnBehaviorError("invalid-on-update", "Column onUpdate metadata must carry the deterministic schema-expression brand", "onUpdate");
|
|
565
|
+
if (defaultDescriptor !== void 0) {
|
|
566
|
+
assertDefaultDescriptor(defaultDescriptor);
|
|
567
|
+
if (options.hasDefault === false) throw new ColumnBehaviorError("default-flag-conflict", "A complete default descriptor cannot be combined with hasDefault: false", "hasDefault");
|
|
568
|
+
if (generatedDescriptor !== void 0 || identityDescriptor !== void 0) throw new ColumnBehaviorError("default-generated-conflict", "A column cannot declare a complete default together with generated-column or identity metadata", "default");
|
|
569
|
+
if (generatedFlag) throw new ColumnBehaviorError("default-generated-conflict", "A complete default descriptor cannot be combined with generated: true", "default");
|
|
570
|
+
}
|
|
571
|
+
if (generatedDescriptor !== void 0) {
|
|
572
|
+
assertGeneratedColumnDescriptor(generatedDescriptor);
|
|
573
|
+
if (options.generated === false) throw new ColumnBehaviorError("generated-flag-conflict", "A complete generated-column descriptor cannot be combined with generated: false", "generated");
|
|
574
|
+
if (identityDescriptor !== void 0) throw new ColumnBehaviorError("identity-generated-conflict", "A column cannot declare both generated-column and identity metadata", "generatedColumn");
|
|
575
|
+
if (hasDefaultFlag) throw new ColumnBehaviorError("default-generated-conflict", "A complete generated-column descriptor cannot be combined with hasDefault: true", "generatedColumn");
|
|
576
|
+
}
|
|
577
|
+
if (identityDescriptor !== void 0) {
|
|
578
|
+
assertIdentityDescriptor(identityDescriptor);
|
|
579
|
+
if (options.generated === false) throw new ColumnBehaviorError("generated-flag-conflict", "Identity metadata cannot be combined with generated: false", "generated");
|
|
580
|
+
if (hasDefaultFlag) throw new ColumnBehaviorError("identity-generated-conflict", "Identity metadata cannot be combined with hasDefault: true", "identity");
|
|
581
|
+
}
|
|
582
|
+
const normalizedDefault = defaultDescriptor === void 0 ? void 0 : freezeDefaultDescriptor(defaultDescriptor);
|
|
583
|
+
const normalizedGenerated = generatedDescriptor === void 0 ? void 0 : freezeGeneratedColumnDescriptor(generatedDescriptor);
|
|
584
|
+
const normalizedIdentity = identityDescriptor === void 0 ? void 0 : freezeIdentityDescriptor(identityDescriptor);
|
|
585
|
+
return Object.freeze({
|
|
586
|
+
hasDefault: hasDefaultFlag || normalizedDefault !== void 0,
|
|
587
|
+
hasRuntimeDefault: defaultFn !== void 0,
|
|
588
|
+
generated: generatedFlag || normalizedGenerated !== void 0 || normalizedIdentity !== void 0,
|
|
589
|
+
default: normalizedDefault ?? (hasDefaultFlag ? externalDefault() : void 0),
|
|
590
|
+
defaultFn,
|
|
591
|
+
generatedColumn: normalizedGenerated ?? (generatedFlag && normalizedIdentity === void 0 ? externalGeneratedColumn() : void 0),
|
|
592
|
+
identity: normalizedIdentity,
|
|
593
|
+
onUpdate: onUpdateExpression
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
function freezeDefaultDescriptor(value) {
|
|
597
|
+
if (value.kind === "external") return externalDefault();
|
|
598
|
+
if (value.kind === "expression") return Object.freeze({
|
|
599
|
+
kind: "expression",
|
|
600
|
+
expression: value.expression
|
|
601
|
+
});
|
|
602
|
+
return Object.freeze({
|
|
603
|
+
kind: "literal",
|
|
604
|
+
value: Object.freeze({ ...value.value })
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
function isExternalDefaultDescriptor(value) {
|
|
608
|
+
return typeof value === "object" && value !== null && value.kind === "external";
|
|
609
|
+
}
|
|
610
|
+
function freezeGeneratedColumnDescriptor(value) {
|
|
611
|
+
if (value.kind === "external") return externalGeneratedColumn();
|
|
612
|
+
return Object.freeze({
|
|
613
|
+
kind: "expression",
|
|
614
|
+
expression: value.expression,
|
|
615
|
+
mode: value.mode
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
function freezeIdentityDescriptor(value) {
|
|
619
|
+
return Object.freeze({
|
|
620
|
+
kind: "identity",
|
|
621
|
+
generation: value.generation,
|
|
622
|
+
...value.options === void 0 ? {} : { options: freezeIdentityOptions(value.options) },
|
|
623
|
+
...value.dialect === void 0 ? {} : { dialect: freezeSchemaMetadata(value.dialect) }
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
function freezeIdentityOptions(value) {
|
|
627
|
+
return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, item]) => [key, item])));
|
|
628
|
+
}
|
|
629
|
+
function assertSchemaExpression(expression, path, code = "invalid-default") {
|
|
630
|
+
if (!isSchemaExpression(expression)) throw new ColumnBehaviorError(code, "Schema behavior expressions must carry the deterministic schema-expression brand", path);
|
|
631
|
+
}
|
|
632
|
+
function assertDefaultDescriptor(value) {
|
|
633
|
+
if (!value || typeof value !== "object") throw new ColumnBehaviorError("invalid-default", "Column default metadata must be an object", "default");
|
|
634
|
+
if (value.kind === "external") return;
|
|
635
|
+
if (value.kind === "expression") {
|
|
636
|
+
assertSchemaExpression(value.expression, "default.expression");
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
if (value.kind === "literal") {
|
|
640
|
+
assertCanonicalLiteral(value.value);
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
throw new ColumnBehaviorError("invalid-default", "Unknown column default descriptor kind", "default.kind");
|
|
644
|
+
}
|
|
645
|
+
function assertGeneratedColumnDescriptor(value) {
|
|
646
|
+
if (!value || typeof value !== "object") throw new ColumnBehaviorError("invalid-generated-column", "Generated-column metadata must be an object", "generatedColumn");
|
|
647
|
+
if (value.kind === "external") return;
|
|
648
|
+
if (value.kind === "expression") {
|
|
649
|
+
assertSchemaExpression(value.expression, "generatedColumn.expression", "invalid-generated-column");
|
|
650
|
+
if (value.mode !== "stored" && value.mode !== "virtual") throw new ColumnBehaviorError("invalid-generated-column", "Generated-column mode must be \"stored\" or \"virtual\"", "generatedColumn.mode");
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
throw new ColumnBehaviorError("invalid-generated-column", "Unknown generated-column descriptor kind", "generatedColumn.kind");
|
|
654
|
+
}
|
|
655
|
+
function assertIdentityDescriptor(value) {
|
|
656
|
+
if (!value || typeof value !== "object" || value.kind !== "identity" || value.generation !== "always" && value.generation !== "by-default") throw new ColumnBehaviorError("invalid-identity", "Identity metadata must use generation \"always\" or \"by-default\"", "identity.generation");
|
|
657
|
+
if (value.dialect !== void 0) {
|
|
658
|
+
if (typeof value.dialect !== "object" || value.dialect === null || typeof value.dialect.dialect !== "string" || value.dialect.dialect.length === 0) throw new ColumnBehaviorError("invalid-identity", "Identity dialect metadata must contain a non-empty dialect tag", "identity.dialect");
|
|
659
|
+
}
|
|
660
|
+
if (value.options !== void 0) {
|
|
661
|
+
if (typeof value.options !== "object" || value.options === null || Array.isArray(value.options)) throw new ColumnBehaviorError("invalid-identity", "Identity options must be an object", "identity.options");
|
|
662
|
+
for (const [key, option] of Object.entries(value.options)) if (typeof option === "number" && !Number.isFinite(option) || typeof option !== "string" && typeof option !== "number" && typeof option !== "boolean" && option !== null && !isSchemaExpression(option)) throw new ColumnBehaviorError("invalid-identity", `Identity option "${key}" must be a finite literal or schema expression`, `identity.options.${key}`);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
function assertCanonicalLiteral(value) {
|
|
666
|
+
if (!value || typeof value !== "object") throw new ColumnBehaviorError("invalid-default", "Literal defaults must contain a canonical literal node", "default.value");
|
|
667
|
+
switch (value.kind) {
|
|
668
|
+
case "null": return;
|
|
669
|
+
case "boolean":
|
|
670
|
+
if (typeof value.value === "boolean") return;
|
|
671
|
+
break;
|
|
672
|
+
case "string":
|
|
673
|
+
if (typeof value.value === "string") return;
|
|
674
|
+
break;
|
|
675
|
+
case "number":
|
|
676
|
+
if (typeof value.value === "string" && value.value.length > 0) return;
|
|
677
|
+
break;
|
|
678
|
+
case "bigint": if (/^-?(0|[1-9][0-9]*)$/.test(value.value)) return;
|
|
679
|
+
}
|
|
680
|
+
throw new ColumnBehaviorError("invalid-default", "Literal defaults must contain a valid canonical literal node", "default.value");
|
|
681
|
+
}
|
|
682
|
+
//#endregion
|
|
683
|
+
//#region src/core/primitives/identifier.ts
|
|
684
|
+
function identifier(name) {
|
|
685
|
+
return fragment((context) => context.append(context.dialect.quoteIdentifier(name)));
|
|
686
|
+
}
|
|
687
|
+
function qualifiedIdentifier(...parts) {
|
|
688
|
+
return fragment((context) => {
|
|
689
|
+
parts.forEach((part, index) => {
|
|
690
|
+
if (index > 0) context.append(".");
|
|
691
|
+
context.append(context.dialect.quoteIdentifier(part));
|
|
692
|
+
});
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
//#endregion
|
|
696
|
+
//#region src/expressions/column.ts
|
|
697
|
+
function createColumnReference(columnName, sourceReference, fieldName, result) {
|
|
698
|
+
const expression = makeSchemaExpression("column", (context) => {
|
|
699
|
+
if (context.renderColumnReference) {
|
|
700
|
+
context.renderColumnReference(columnName);
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
context.render(sourceReference);
|
|
704
|
+
context.append(".");
|
|
705
|
+
context.render(identifier(columnName));
|
|
706
|
+
}, result);
|
|
707
|
+
return Object.freeze({
|
|
708
|
+
...expression,
|
|
709
|
+
fieldName,
|
|
710
|
+
columnName
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
function isColumnReference(value) {
|
|
714
|
+
return typeof value === "object" && value !== null && "expressionKind" in value && value.expressionKind === "column";
|
|
715
|
+
}
|
|
716
|
+
/** Turn an expression into a fragment that renders it without changing it. */
|
|
717
|
+
function expressionFragment(expression) {
|
|
718
|
+
return fragment((context) => context.render(expression));
|
|
719
|
+
}
|
|
720
|
+
//#endregion
|
|
721
|
+
export { resolveCastTarget as $, fragment as A, decodeResultRow as B, isExpression as C, markExpressionCategory as D, makeSchemaExpression as E, attachResultValue as F, timestampResultDecoder as G, resultShapeValue as H, bigintResultDecoder as I, sqliteJson as J, mysqlJson as K, booleanResultDecoder as L, parenthesize as M, sequence as N, markSchemaExpression as O, ResultDecodingError as P, extendDialect as Q, createResultShape as R, snakeCaseIdentifier as S, makeExpression as T, resultValue as U, jsonTextResultDecoder as V, resultValueOf as W, assertDialectCapability as X, standardJson as Y, createDialect as Z, generatedSchemaObjectName as _, qualifiedIdentifier as a, materializeSchemaObjectRecord as b, externalDefault as c, identityColumn as d, resolveColumnBehavior as f, freezeSchemaMetadata as g, dialectMismatchDiagnostic as h, identifier as i, isFragment as j, withDialectCapability as k, externalGeneratedColumn as l, assertSchemaDialectSupport as m, expressionFragment as n, ColumnBehaviorError as o, SchemaMetadataValidationError as p, postgresJson as q, isColumnReference as r, canonicalLiteral as s, createColumnReference as t, generatedColumn as u, isValidSchemaObjectName as v, isSchemaExpression as w, resolveSqlNames as x, materializeSchemaObjectIdentity as y, dateResultDecoder as z };
|