sqlite-zod-orm 3.8.0 → 3.9.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.
- package/README.md +146 -93
- package/dist/index.js +1631 -1604
- package/package.json +5 -3
- package/src/ast.ts +1 -1
- package/src/context.ts +25 -0
- package/src/crud.ts +145 -0
- package/src/database.ts +170 -396
- package/src/entity.ts +62 -0
- package/src/helpers.ts +87 -0
- package/src/index.ts +2 -3
- package/src/query.ts +856 -0
- package/src/types.ts +19 -4
- package/dist/satidb.js +0 -26
- package/src/build.ts +0 -21
- package/src/proxy-query.ts +0 -312
- package/src/query-builder.ts +0 -669
package/dist/index.js
CHANGED
|
@@ -72,585 +72,6 @@ var op = {
|
|
|
72
72
|
not: (node) => ({ type: "operator", op: "NOT", left: { type: "literal", value: "" }, right: wrapNode(node) })
|
|
73
73
|
};
|
|
74
74
|
|
|
75
|
-
// src/query-builder.ts
|
|
76
|
-
var OPERATOR_MAP = {
|
|
77
|
-
$gt: ">",
|
|
78
|
-
$gte: ">=",
|
|
79
|
-
$lt: "<",
|
|
80
|
-
$lte: "<=",
|
|
81
|
-
$ne: "!=",
|
|
82
|
-
$in: "IN"
|
|
83
|
-
};
|
|
84
|
-
function transformValueForStorage(value) {
|
|
85
|
-
if (value instanceof Date)
|
|
86
|
-
return value.toISOString();
|
|
87
|
-
if (typeof value === "boolean")
|
|
88
|
-
return value ? 1 : 0;
|
|
89
|
-
return value;
|
|
90
|
-
}
|
|
91
|
-
function compileIQO(tableName, iqo) {
|
|
92
|
-
const params = [];
|
|
93
|
-
const selectParts = [];
|
|
94
|
-
if (iqo.selects.length > 0) {
|
|
95
|
-
selectParts.push(...iqo.selects.map((s) => `${tableName}.${s}`));
|
|
96
|
-
} else {
|
|
97
|
-
selectParts.push(`${tableName}.*`);
|
|
98
|
-
}
|
|
99
|
-
for (const j of iqo.joins) {
|
|
100
|
-
if (j.columns.length > 0) {
|
|
101
|
-
selectParts.push(...j.columns.map((c) => `${j.table}.${c} AS ${j.table}_${c}`));
|
|
102
|
-
} else {
|
|
103
|
-
selectParts.push(`${j.table}.*`);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
let sql = `SELECT ${selectParts.join(", ")} FROM ${tableName}`;
|
|
107
|
-
for (const j of iqo.joins) {
|
|
108
|
-
sql += ` JOIN ${j.table} ON ${tableName}.${j.fromCol} = ${j.table}.${j.toCol}`;
|
|
109
|
-
}
|
|
110
|
-
if (iqo.whereAST) {
|
|
111
|
-
const compiled = compileAST(iqo.whereAST);
|
|
112
|
-
sql += ` WHERE ${compiled.sql}`;
|
|
113
|
-
params.push(...compiled.params);
|
|
114
|
-
} else if (iqo.wheres.length > 0) {
|
|
115
|
-
const hasJoins = iqo.joins.length > 0;
|
|
116
|
-
const qualify = (field) => hasJoins && !field.includes(".") ? `${tableName}.${field}` : field;
|
|
117
|
-
const whereParts = [];
|
|
118
|
-
for (const w of iqo.wheres) {
|
|
119
|
-
if (w.operator === "IN") {
|
|
120
|
-
const arr = w.value;
|
|
121
|
-
if (arr.length === 0) {
|
|
122
|
-
whereParts.push("1 = 0");
|
|
123
|
-
} else {
|
|
124
|
-
const placeholders = arr.map(() => "?").join(", ");
|
|
125
|
-
whereParts.push(`${qualify(w.field)} IN (${placeholders})`);
|
|
126
|
-
params.push(...arr.map(transformValueForStorage));
|
|
127
|
-
}
|
|
128
|
-
} else {
|
|
129
|
-
whereParts.push(`${qualify(w.field)} ${w.operator} ?`);
|
|
130
|
-
params.push(transformValueForStorage(w.value));
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
sql += ` WHERE ${whereParts.join(" AND ")}`;
|
|
134
|
-
}
|
|
135
|
-
if (iqo.whereOrs.length > 0) {
|
|
136
|
-
for (const orGroup of iqo.whereOrs) {
|
|
137
|
-
const orParts = [];
|
|
138
|
-
for (const w of orGroup) {
|
|
139
|
-
if (w.operator === "IN") {
|
|
140
|
-
const arr = w.value;
|
|
141
|
-
if (arr.length === 0) {
|
|
142
|
-
orParts.push("1 = 0");
|
|
143
|
-
} else {
|
|
144
|
-
orParts.push(`${w.field} IN (${arr.map(() => "?").join(", ")})`);
|
|
145
|
-
params.push(...arr.map(transformValueForStorage));
|
|
146
|
-
}
|
|
147
|
-
} else {
|
|
148
|
-
orParts.push(`${w.field} ${w.operator} ?`);
|
|
149
|
-
params.push(transformValueForStorage(w.value));
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
if (orParts.length > 0) {
|
|
153
|
-
const orClause = `(${orParts.join(" OR ")})`;
|
|
154
|
-
sql += sql.includes(" WHERE ") ? ` AND ${orClause}` : ` WHERE ${orClause}`;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
if (iqo.orderBy.length > 0) {
|
|
159
|
-
const parts = iqo.orderBy.map((o) => `${o.field} ${o.direction.toUpperCase()}`);
|
|
160
|
-
sql += ` ORDER BY ${parts.join(", ")}`;
|
|
161
|
-
}
|
|
162
|
-
if (iqo.limit !== null) {
|
|
163
|
-
sql += ` LIMIT ${iqo.limit}`;
|
|
164
|
-
}
|
|
165
|
-
if (iqo.offset !== null) {
|
|
166
|
-
sql += ` OFFSET ${iqo.offset}`;
|
|
167
|
-
}
|
|
168
|
-
return { sql, params };
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
class QueryBuilder {
|
|
172
|
-
iqo;
|
|
173
|
-
tableName;
|
|
174
|
-
executor;
|
|
175
|
-
singleExecutor;
|
|
176
|
-
joinResolver;
|
|
177
|
-
conditionResolver;
|
|
178
|
-
revisionGetter;
|
|
179
|
-
eagerLoader;
|
|
180
|
-
defaultPollInterval;
|
|
181
|
-
constructor(tableName, executor, singleExecutor, joinResolver, conditionResolver, revisionGetter, eagerLoader, pollInterval) {
|
|
182
|
-
this.tableName = tableName;
|
|
183
|
-
this.executor = executor;
|
|
184
|
-
this.singleExecutor = singleExecutor;
|
|
185
|
-
this.joinResolver = joinResolver ?? null;
|
|
186
|
-
this.conditionResolver = conditionResolver ?? null;
|
|
187
|
-
this.revisionGetter = revisionGetter ?? null;
|
|
188
|
-
this.eagerLoader = eagerLoader ?? null;
|
|
189
|
-
this.defaultPollInterval = pollInterval ?? 500;
|
|
190
|
-
this.iqo = {
|
|
191
|
-
selects: [],
|
|
192
|
-
wheres: [],
|
|
193
|
-
whereOrs: [],
|
|
194
|
-
whereAST: null,
|
|
195
|
-
joins: [],
|
|
196
|
-
limit: null,
|
|
197
|
-
offset: null,
|
|
198
|
-
orderBy: [],
|
|
199
|
-
includes: [],
|
|
200
|
-
raw: false
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
select(...cols) {
|
|
204
|
-
this.iqo.selects.push(...cols);
|
|
205
|
-
return this;
|
|
206
|
-
}
|
|
207
|
-
where(criteriaOrCallback) {
|
|
208
|
-
if (typeof criteriaOrCallback === "function") {
|
|
209
|
-
const ast = criteriaOrCallback(createColumnProxy(), createFunctionProxy(), op);
|
|
210
|
-
if (this.iqo.whereAST) {
|
|
211
|
-
this.iqo.whereAST = { type: "operator", op: "AND", left: this.iqo.whereAST, right: ast };
|
|
212
|
-
} else {
|
|
213
|
-
this.iqo.whereAST = ast;
|
|
214
|
-
}
|
|
215
|
-
} else {
|
|
216
|
-
const resolved = this.conditionResolver ? this.conditionResolver(criteriaOrCallback) : criteriaOrCallback;
|
|
217
|
-
for (const [key, value] of Object.entries(resolved)) {
|
|
218
|
-
if (key === "$or" && Array.isArray(value)) {
|
|
219
|
-
const orConditions = [];
|
|
220
|
-
for (const branch of value) {
|
|
221
|
-
const resolvedBranch = this.conditionResolver ? this.conditionResolver(branch) : branch;
|
|
222
|
-
for (const [bKey, bValue] of Object.entries(resolvedBranch)) {
|
|
223
|
-
if (typeof bValue === "object" && bValue !== null && !Array.isArray(bValue) && !(bValue instanceof Date)) {
|
|
224
|
-
for (const [opKey, operand] of Object.entries(bValue)) {
|
|
225
|
-
const sqlOp = OPERATOR_MAP[opKey];
|
|
226
|
-
if (sqlOp)
|
|
227
|
-
orConditions.push({ field: bKey, operator: sqlOp, value: operand });
|
|
228
|
-
}
|
|
229
|
-
} else {
|
|
230
|
-
orConditions.push({ field: bKey, operator: "=", value: bValue });
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
if (orConditions.length > 0)
|
|
235
|
-
this.iqo.whereOrs.push(orConditions);
|
|
236
|
-
continue;
|
|
237
|
-
}
|
|
238
|
-
if (typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date)) {
|
|
239
|
-
for (const [opKey, operand] of Object.entries(value)) {
|
|
240
|
-
const sqlOp = OPERATOR_MAP[opKey];
|
|
241
|
-
if (!sqlOp)
|
|
242
|
-
throw new Error(`Unsupported query operator: '${opKey}' on field '${key}'.`);
|
|
243
|
-
this.iqo.wheres.push({
|
|
244
|
-
field: key,
|
|
245
|
-
operator: sqlOp,
|
|
246
|
-
value: operand
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
} else {
|
|
250
|
-
this.iqo.wheres.push({ field: key, operator: "=", value });
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
return this;
|
|
255
|
-
}
|
|
256
|
-
limit(n) {
|
|
257
|
-
this.iqo.limit = n;
|
|
258
|
-
return this;
|
|
259
|
-
}
|
|
260
|
-
offset(n) {
|
|
261
|
-
this.iqo.offset = n;
|
|
262
|
-
return this;
|
|
263
|
-
}
|
|
264
|
-
orderBy(field, direction = "asc") {
|
|
265
|
-
this.iqo.orderBy.push({ field, direction });
|
|
266
|
-
return this;
|
|
267
|
-
}
|
|
268
|
-
join(tableOrAccessor, fkOrCols, colsOrPk, pk) {
|
|
269
|
-
let table;
|
|
270
|
-
let fromCol;
|
|
271
|
-
let toCol;
|
|
272
|
-
let columns;
|
|
273
|
-
if (typeof tableOrAccessor === "object" && "_tableName" in tableOrAccessor) {
|
|
274
|
-
table = tableOrAccessor._tableName;
|
|
275
|
-
columns = Array.isArray(fkOrCols) ? fkOrCols : [];
|
|
276
|
-
if (!this.joinResolver)
|
|
277
|
-
throw new Error(`Cannot auto-resolve join: no relationship data available`);
|
|
278
|
-
const resolved = this.joinResolver(this.tableName, table);
|
|
279
|
-
if (!resolved)
|
|
280
|
-
throw new Error(`No relationship found between '${this.tableName}' and '${table}'`);
|
|
281
|
-
fromCol = resolved.fk;
|
|
282
|
-
toCol = resolved.pk;
|
|
283
|
-
} else {
|
|
284
|
-
table = tableOrAccessor;
|
|
285
|
-
fromCol = fkOrCols;
|
|
286
|
-
columns = Array.isArray(colsOrPk) ? colsOrPk : [];
|
|
287
|
-
toCol = (typeof colsOrPk === "string" ? colsOrPk : pk) ?? "id";
|
|
288
|
-
}
|
|
289
|
-
this.iqo.joins.push({ table, fromCol, toCol, columns });
|
|
290
|
-
this.iqo.raw = true;
|
|
291
|
-
return this;
|
|
292
|
-
}
|
|
293
|
-
raw() {
|
|
294
|
-
this.iqo.raw = true;
|
|
295
|
-
return this;
|
|
296
|
-
}
|
|
297
|
-
with(...relations) {
|
|
298
|
-
this.iqo.includes.push(...relations);
|
|
299
|
-
return this;
|
|
300
|
-
}
|
|
301
|
-
_applyEagerLoads(results) {
|
|
302
|
-
if (this.iqo.includes.length === 0 || !this.eagerLoader || results.length === 0) {
|
|
303
|
-
return results;
|
|
304
|
-
}
|
|
305
|
-
const parentIds = results.map((r) => r.id).filter((id) => typeof id === "number");
|
|
306
|
-
if (parentIds.length === 0)
|
|
307
|
-
return results;
|
|
308
|
-
for (const relation of this.iqo.includes) {
|
|
309
|
-
const loaded = this.eagerLoader(this.tableName, relation, parentIds);
|
|
310
|
-
if (!loaded)
|
|
311
|
-
continue;
|
|
312
|
-
for (const row of results) {
|
|
313
|
-
row[loaded.key] = loaded.groups.get(row.id) ?? [];
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
return results;
|
|
317
|
-
}
|
|
318
|
-
all() {
|
|
319
|
-
const { sql, params } = compileIQO(this.tableName, this.iqo);
|
|
320
|
-
const results = this.executor(sql, params, this.iqo.raw);
|
|
321
|
-
return this._applyEagerLoads(results);
|
|
322
|
-
}
|
|
323
|
-
get() {
|
|
324
|
-
this.iqo.limit = 1;
|
|
325
|
-
const { sql, params } = compileIQO(this.tableName, this.iqo);
|
|
326
|
-
const result = this.singleExecutor(sql, params, this.iqo.raw);
|
|
327
|
-
if (!result)
|
|
328
|
-
return null;
|
|
329
|
-
const [loaded] = this._applyEagerLoads([result]);
|
|
330
|
-
return loaded ?? null;
|
|
331
|
-
}
|
|
332
|
-
count() {
|
|
333
|
-
const params = [];
|
|
334
|
-
let sql = `SELECT COUNT(*) as count FROM ${this.tableName}`;
|
|
335
|
-
if (this.iqo.whereAST) {
|
|
336
|
-
const compiled = compileAST(this.iqo.whereAST);
|
|
337
|
-
sql += ` WHERE ${compiled.sql}`;
|
|
338
|
-
params.push(...compiled.params);
|
|
339
|
-
} else if (this.iqo.wheres.length > 0) {
|
|
340
|
-
const whereParts = [];
|
|
341
|
-
for (const w of this.iqo.wheres) {
|
|
342
|
-
if (w.operator === "IN") {
|
|
343
|
-
const arr = w.value;
|
|
344
|
-
if (arr.length === 0) {
|
|
345
|
-
whereParts.push("1 = 0");
|
|
346
|
-
} else {
|
|
347
|
-
const placeholders = arr.map(() => "?").join(", ");
|
|
348
|
-
whereParts.push(`${w.field} IN (${placeholders})`);
|
|
349
|
-
params.push(...arr.map(transformValueForStorage));
|
|
350
|
-
}
|
|
351
|
-
} else {
|
|
352
|
-
whereParts.push(`${w.field} ${w.operator} ?`);
|
|
353
|
-
params.push(transformValueForStorage(w.value));
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
sql += ` WHERE ${whereParts.join(" AND ")}`;
|
|
357
|
-
}
|
|
358
|
-
const results = this.executor(sql, params, true);
|
|
359
|
-
return results[0]?.count ?? 0;
|
|
360
|
-
}
|
|
361
|
-
subscribe(callback, options = {}) {
|
|
362
|
-
const { interval = this.defaultPollInterval, immediate = true } = options;
|
|
363
|
-
let lastRevision = null;
|
|
364
|
-
let stopped = false;
|
|
365
|
-
const poll = async () => {
|
|
366
|
-
if (stopped)
|
|
367
|
-
return;
|
|
368
|
-
try {
|
|
369
|
-
const rev = this.revisionGetter?.() ?? "0";
|
|
370
|
-
if (rev !== lastRevision) {
|
|
371
|
-
lastRevision = rev;
|
|
372
|
-
const rows = this.all();
|
|
373
|
-
await callback(rows);
|
|
374
|
-
}
|
|
375
|
-
} catch {}
|
|
376
|
-
if (!stopped)
|
|
377
|
-
setTimeout(poll, interval);
|
|
378
|
-
};
|
|
379
|
-
if (immediate) {
|
|
380
|
-
poll();
|
|
381
|
-
} else {
|
|
382
|
-
setTimeout(poll, interval);
|
|
383
|
-
}
|
|
384
|
-
return () => {
|
|
385
|
-
stopped = true;
|
|
386
|
-
};
|
|
387
|
-
}
|
|
388
|
-
each(callback, options = {}) {
|
|
389
|
-
const { interval = this.defaultPollInterval } = options;
|
|
390
|
-
const userWhere = this.buildWhereClause();
|
|
391
|
-
const maxRows = this.executor(`SELECT MAX(id) as _max FROM ${this.tableName} ${userWhere.sql ? `WHERE ${userWhere.sql}` : ""}`, userWhere.params, true);
|
|
392
|
-
let lastMaxId = maxRows[0]?._max ?? 0;
|
|
393
|
-
let lastRevision = this.revisionGetter?.() ?? "0";
|
|
394
|
-
let stopped = false;
|
|
395
|
-
const poll = async () => {
|
|
396
|
-
if (stopped)
|
|
397
|
-
return;
|
|
398
|
-
const rev = this.revisionGetter?.() ?? "0";
|
|
399
|
-
if (rev !== lastRevision) {
|
|
400
|
-
lastRevision = rev;
|
|
401
|
-
const params = [...userWhere.params, lastMaxId];
|
|
402
|
-
const whereClause = userWhere.sql ? `WHERE ${userWhere.sql} AND id > ? ORDER BY id ASC` : `WHERE id > ? ORDER BY id ASC`;
|
|
403
|
-
const sql = `SELECT * FROM ${this.tableName} ${whereClause}`;
|
|
404
|
-
const newRows = this.executor(sql, params, false);
|
|
405
|
-
for (const row of newRows) {
|
|
406
|
-
if (stopped)
|
|
407
|
-
return;
|
|
408
|
-
await callback(row);
|
|
409
|
-
lastMaxId = row.id;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
if (!stopped)
|
|
413
|
-
setTimeout(poll, interval);
|
|
414
|
-
};
|
|
415
|
-
setTimeout(poll, interval);
|
|
416
|
-
return () => {
|
|
417
|
-
stopped = true;
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
|
-
buildWhereClause() {
|
|
421
|
-
const params = [];
|
|
422
|
-
if (this.iqo.whereAST) {
|
|
423
|
-
const compiled = compileAST(this.iqo.whereAST);
|
|
424
|
-
return { sql: compiled.sql, params: compiled.params };
|
|
425
|
-
}
|
|
426
|
-
if (this.iqo.wheres.length > 0) {
|
|
427
|
-
const whereParts = [];
|
|
428
|
-
for (const w of this.iqo.wheres) {
|
|
429
|
-
if (w.operator === "IN") {
|
|
430
|
-
const arr = w.value;
|
|
431
|
-
if (arr.length === 0) {
|
|
432
|
-
whereParts.push("1 = 0");
|
|
433
|
-
} else {
|
|
434
|
-
const placeholders = arr.map(() => "?").join(", ");
|
|
435
|
-
whereParts.push(`${w.field} IN (${placeholders})`);
|
|
436
|
-
params.push(...arr.map(transformValueForStorage));
|
|
437
|
-
}
|
|
438
|
-
} else {
|
|
439
|
-
whereParts.push(`${w.field} ${w.operator} ?`);
|
|
440
|
-
params.push(transformValueForStorage(w.value));
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
return { sql: whereParts.join(" AND "), params };
|
|
444
|
-
}
|
|
445
|
-
return { sql: "", params: [] };
|
|
446
|
-
}
|
|
447
|
-
then(onfulfilled, onrejected) {
|
|
448
|
-
try {
|
|
449
|
-
const result = this.all();
|
|
450
|
-
return Promise.resolve(result).then(onfulfilled, onrejected);
|
|
451
|
-
} catch (err) {
|
|
452
|
-
return Promise.reject(err).then(onfulfilled, onrejected);
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
// src/proxy-query.ts
|
|
458
|
-
class ColumnNode {
|
|
459
|
-
table;
|
|
460
|
-
column;
|
|
461
|
-
alias;
|
|
462
|
-
_type = "COL";
|
|
463
|
-
constructor(table, column, alias) {
|
|
464
|
-
this.table = table;
|
|
465
|
-
this.column = column;
|
|
466
|
-
this.alias = alias;
|
|
467
|
-
}
|
|
468
|
-
toString() {
|
|
469
|
-
return `"${this.alias}"."${this.column}"`;
|
|
470
|
-
}
|
|
471
|
-
[Symbol.toPrimitive]() {
|
|
472
|
-
return this.toString();
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
function q(name) {
|
|
476
|
-
return `"${name}"`;
|
|
477
|
-
}
|
|
478
|
-
function qRef(alias, column) {
|
|
479
|
-
return `"${alias}"."${column}"`;
|
|
480
|
-
}
|
|
481
|
-
function createTableProxy(tableName, alias, columns) {
|
|
482
|
-
return new Proxy({}, {
|
|
483
|
-
get(_target, prop) {
|
|
484
|
-
if (prop === Symbol.toPrimitive || prop === "toString" || prop === "valueOf") {
|
|
485
|
-
return;
|
|
486
|
-
}
|
|
487
|
-
return new ColumnNode(tableName, prop, alias);
|
|
488
|
-
},
|
|
489
|
-
ownKeys() {
|
|
490
|
-
return [...columns];
|
|
491
|
-
},
|
|
492
|
-
getOwnPropertyDescriptor(_target, prop) {
|
|
493
|
-
if (columns.has(prop)) {
|
|
494
|
-
return { configurable: true, enumerable: true, value: new ColumnNode(tableName, prop, alias) };
|
|
495
|
-
}
|
|
496
|
-
return;
|
|
497
|
-
}
|
|
498
|
-
});
|
|
499
|
-
}
|
|
500
|
-
function createContextProxy(schemas) {
|
|
501
|
-
const aliases = new Map;
|
|
502
|
-
let aliasCounter = 0;
|
|
503
|
-
const proxy = new Proxy({}, {
|
|
504
|
-
get(_target, tableName) {
|
|
505
|
-
if (typeof tableName !== "string")
|
|
506
|
-
return;
|
|
507
|
-
const schema = schemas[tableName];
|
|
508
|
-
const shape = schema ? schema.shape : {};
|
|
509
|
-
const columns = new Set(Object.keys(shape));
|
|
510
|
-
aliasCounter++;
|
|
511
|
-
const alias = `t${aliasCounter}`;
|
|
512
|
-
const tableProxy = createTableProxy(tableName, alias, columns);
|
|
513
|
-
const entries = aliases.get(tableName) || [];
|
|
514
|
-
entries.push({ tableName, alias, proxy: tableProxy });
|
|
515
|
-
aliases.set(tableName, entries);
|
|
516
|
-
return tableProxy;
|
|
517
|
-
}
|
|
518
|
-
});
|
|
519
|
-
return { proxy, aliasMap: aliases };
|
|
520
|
-
}
|
|
521
|
-
function isColumnNode(val) {
|
|
522
|
-
return val && typeof val === "object" && val._type === "COL";
|
|
523
|
-
}
|
|
524
|
-
function compileProxyQuery(queryResult, aliasMap) {
|
|
525
|
-
const params = [];
|
|
526
|
-
const tablesUsed = new Map;
|
|
527
|
-
for (const [tableName, entries] of aliasMap) {
|
|
528
|
-
for (const entry of entries) {
|
|
529
|
-
tablesUsed.set(entry.alias, { tableName, alias: entry.alias });
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
const selectParts = [];
|
|
533
|
-
for (const [outputName, colOrValue] of Object.entries(queryResult.select)) {
|
|
534
|
-
if (isColumnNode(colOrValue)) {
|
|
535
|
-
if (outputName === colOrValue.column) {
|
|
536
|
-
selectParts.push(qRef(colOrValue.alias, colOrValue.column));
|
|
537
|
-
} else {
|
|
538
|
-
selectParts.push(`${qRef(colOrValue.alias, colOrValue.column)} AS ${q(outputName)}`);
|
|
539
|
-
}
|
|
540
|
-
} else {
|
|
541
|
-
selectParts.push(`? AS ${q(outputName)}`);
|
|
542
|
-
params.push(colOrValue);
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
const allAliases = [...tablesUsed.values()];
|
|
546
|
-
if (allAliases.length === 0)
|
|
547
|
-
throw new Error("No tables referenced in query.");
|
|
548
|
-
const primaryAlias = allAliases[0];
|
|
549
|
-
let sql = `SELECT ${selectParts.join(", ")} FROM ${q(primaryAlias.tableName)} ${q(primaryAlias.alias)}`;
|
|
550
|
-
if (queryResult.join) {
|
|
551
|
-
const joins = Array.isArray(queryResult.join[0]) ? queryResult.join : [queryResult.join];
|
|
552
|
-
for (const [left, right] of joins) {
|
|
553
|
-
const leftTable = tablesUsed.get(left.alias);
|
|
554
|
-
const rightTable = tablesUsed.get(right.alias);
|
|
555
|
-
if (!leftTable || !rightTable)
|
|
556
|
-
throw new Error("Join references unknown table alias.");
|
|
557
|
-
const joinAlias = leftTable.alias === primaryAlias.alias ? rightTable : leftTable;
|
|
558
|
-
sql += ` JOIN ${q(joinAlias.tableName)} ${q(joinAlias.alias)} ON ${qRef(left.alias, left.column)} = ${qRef(right.alias, right.column)}`;
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
if (queryResult.where && Object.keys(queryResult.where).length > 0) {
|
|
562
|
-
const whereParts = [];
|
|
563
|
-
for (const [key, value] of Object.entries(queryResult.where)) {
|
|
564
|
-
let fieldRef;
|
|
565
|
-
const quotedMatch = key.match(/^"([^"]+)"\."([^"]+)"$/);
|
|
566
|
-
if (quotedMatch && tablesUsed.has(quotedMatch[1])) {
|
|
567
|
-
fieldRef = key;
|
|
568
|
-
} else {
|
|
569
|
-
fieldRef = qRef(primaryAlias.alias, key);
|
|
570
|
-
}
|
|
571
|
-
if (isColumnNode(value)) {
|
|
572
|
-
whereParts.push(`${fieldRef} = ${qRef(value.alias, value.column)}`);
|
|
573
|
-
} else if (Array.isArray(value)) {
|
|
574
|
-
if (value.length === 0) {
|
|
575
|
-
whereParts.push("1 = 0");
|
|
576
|
-
} else {
|
|
577
|
-
const placeholders = value.map(() => "?").join(", ");
|
|
578
|
-
whereParts.push(`${fieldRef} IN (${placeholders})`);
|
|
579
|
-
params.push(...value);
|
|
580
|
-
}
|
|
581
|
-
} else if (typeof value === "object" && value !== null && !(value instanceof Date)) {
|
|
582
|
-
for (const [op2, operand] of Object.entries(value)) {
|
|
583
|
-
if (op2 === "$in") {
|
|
584
|
-
const arr = operand;
|
|
585
|
-
if (arr.length === 0) {
|
|
586
|
-
whereParts.push("1 = 0");
|
|
587
|
-
} else {
|
|
588
|
-
const placeholders = arr.map(() => "?").join(", ");
|
|
589
|
-
whereParts.push(`${fieldRef} IN (${placeholders})`);
|
|
590
|
-
params.push(...arr);
|
|
591
|
-
}
|
|
592
|
-
continue;
|
|
593
|
-
}
|
|
594
|
-
const opMap = {
|
|
595
|
-
$gt: ">",
|
|
596
|
-
$gte: ">=",
|
|
597
|
-
$lt: "<",
|
|
598
|
-
$lte: "<=",
|
|
599
|
-
$ne: "!="
|
|
600
|
-
};
|
|
601
|
-
const sqlOp = opMap[op2];
|
|
602
|
-
if (!sqlOp)
|
|
603
|
-
throw new Error(`Unsupported where operator: ${op2}`);
|
|
604
|
-
whereParts.push(`${fieldRef} ${sqlOp} ?`);
|
|
605
|
-
params.push(operand);
|
|
606
|
-
}
|
|
607
|
-
} else {
|
|
608
|
-
whereParts.push(`${fieldRef} = ?`);
|
|
609
|
-
params.push(value instanceof Date ? value.toISOString() : value);
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
if (whereParts.length > 0) {
|
|
613
|
-
sql += ` WHERE ${whereParts.join(" AND ")}`;
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
if (queryResult.orderBy) {
|
|
617
|
-
const parts = [];
|
|
618
|
-
for (const [key, dir] of Object.entries(queryResult.orderBy)) {
|
|
619
|
-
let fieldRef;
|
|
620
|
-
const quotedMatch = key.match(/^"([^"]+)"\."([^"]+)"$/);
|
|
621
|
-
if (quotedMatch && tablesUsed.has(quotedMatch[1])) {
|
|
622
|
-
fieldRef = key;
|
|
623
|
-
} else {
|
|
624
|
-
fieldRef = qRef(primaryAlias.alias, key);
|
|
625
|
-
}
|
|
626
|
-
parts.push(`${fieldRef} ${dir.toUpperCase()}`);
|
|
627
|
-
}
|
|
628
|
-
if (parts.length > 0) {
|
|
629
|
-
sql += ` ORDER BY ${parts.join(", ")}`;
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
if (queryResult.groupBy && queryResult.groupBy.length > 0) {
|
|
633
|
-
const parts = queryResult.groupBy.filter(Boolean).map((col) => qRef(col.alias, col.column));
|
|
634
|
-
sql += ` GROUP BY ${parts.join(", ")}`;
|
|
635
|
-
}
|
|
636
|
-
if (queryResult.limit !== undefined) {
|
|
637
|
-
sql += ` LIMIT ${queryResult.limit}`;
|
|
638
|
-
}
|
|
639
|
-
if (queryResult.offset !== undefined) {
|
|
640
|
-
sql += ` OFFSET ${queryResult.offset}`;
|
|
641
|
-
}
|
|
642
|
-
return { sql, params };
|
|
643
|
-
}
|
|
644
|
-
function executeProxyQuery(schemas, callback, executor) {
|
|
645
|
-
const { proxy, aliasMap } = createContextProxy(schemas);
|
|
646
|
-
const queryResult = callback(proxy);
|
|
647
|
-
const { sql, params } = compileProxyQuery(queryResult, aliasMap);
|
|
648
|
-
return executor(sql, params);
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
// src/types.ts
|
|
652
|
-
var asZodObject = (s) => s;
|
|
653
|
-
|
|
654
75
|
// node_modules/zod/v3/external.js
|
|
655
76
|
var exports_external = {};
|
|
656
77
|
__export(exports_external, {
|
|
@@ -3837,917 +3258,1718 @@ ZodSet.create = (valueType, params) => {
|
|
|
3837
3258
|
});
|
|
3838
3259
|
};
|
|
3839
3260
|
|
|
3840
|
-
class ZodFunction extends ZodType {
|
|
3841
|
-
constructor() {
|
|
3842
|
-
super(...arguments);
|
|
3843
|
-
this.validate = this.implement;
|
|
3261
|
+
class ZodFunction extends ZodType {
|
|
3262
|
+
constructor() {
|
|
3263
|
+
super(...arguments);
|
|
3264
|
+
this.validate = this.implement;
|
|
3265
|
+
}
|
|
3266
|
+
_parse(input) {
|
|
3267
|
+
const { ctx } = this._processInputParams(input);
|
|
3268
|
+
if (ctx.parsedType !== ZodParsedType.function) {
|
|
3269
|
+
addIssueToContext(ctx, {
|
|
3270
|
+
code: ZodIssueCode.invalid_type,
|
|
3271
|
+
expected: ZodParsedType.function,
|
|
3272
|
+
received: ctx.parsedType
|
|
3273
|
+
});
|
|
3274
|
+
return INVALID;
|
|
3275
|
+
}
|
|
3276
|
+
function makeArgsIssue(args, error) {
|
|
3277
|
+
return makeIssue({
|
|
3278
|
+
data: args,
|
|
3279
|
+
path: ctx.path,
|
|
3280
|
+
errorMaps: [ctx.common.contextualErrorMap, ctx.schemaErrorMap, getErrorMap(), en_default].filter((x) => !!x),
|
|
3281
|
+
issueData: {
|
|
3282
|
+
code: ZodIssueCode.invalid_arguments,
|
|
3283
|
+
argumentsError: error
|
|
3284
|
+
}
|
|
3285
|
+
});
|
|
3286
|
+
}
|
|
3287
|
+
function makeReturnsIssue(returns, error) {
|
|
3288
|
+
return makeIssue({
|
|
3289
|
+
data: returns,
|
|
3290
|
+
path: ctx.path,
|
|
3291
|
+
errorMaps: [ctx.common.contextualErrorMap, ctx.schemaErrorMap, getErrorMap(), en_default].filter((x) => !!x),
|
|
3292
|
+
issueData: {
|
|
3293
|
+
code: ZodIssueCode.invalid_return_type,
|
|
3294
|
+
returnTypeError: error
|
|
3295
|
+
}
|
|
3296
|
+
});
|
|
3297
|
+
}
|
|
3298
|
+
const params = { errorMap: ctx.common.contextualErrorMap };
|
|
3299
|
+
const fn = ctx.data;
|
|
3300
|
+
if (this._def.returns instanceof ZodPromise) {
|
|
3301
|
+
const me = this;
|
|
3302
|
+
return OK(async function(...args) {
|
|
3303
|
+
const error = new ZodError([]);
|
|
3304
|
+
const parsedArgs = await me._def.args.parseAsync(args, params).catch((e) => {
|
|
3305
|
+
error.addIssue(makeArgsIssue(args, e));
|
|
3306
|
+
throw error;
|
|
3307
|
+
});
|
|
3308
|
+
const result = await Reflect.apply(fn, this, parsedArgs);
|
|
3309
|
+
const parsedReturns = await me._def.returns._def.type.parseAsync(result, params).catch((e) => {
|
|
3310
|
+
error.addIssue(makeReturnsIssue(result, e));
|
|
3311
|
+
throw error;
|
|
3312
|
+
});
|
|
3313
|
+
return parsedReturns;
|
|
3314
|
+
});
|
|
3315
|
+
} else {
|
|
3316
|
+
const me = this;
|
|
3317
|
+
return OK(function(...args) {
|
|
3318
|
+
const parsedArgs = me._def.args.safeParse(args, params);
|
|
3319
|
+
if (!parsedArgs.success) {
|
|
3320
|
+
throw new ZodError([makeArgsIssue(args, parsedArgs.error)]);
|
|
3321
|
+
}
|
|
3322
|
+
const result = Reflect.apply(fn, this, parsedArgs.data);
|
|
3323
|
+
const parsedReturns = me._def.returns.safeParse(result, params);
|
|
3324
|
+
if (!parsedReturns.success) {
|
|
3325
|
+
throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]);
|
|
3326
|
+
}
|
|
3327
|
+
return parsedReturns.data;
|
|
3328
|
+
});
|
|
3329
|
+
}
|
|
3330
|
+
}
|
|
3331
|
+
parameters() {
|
|
3332
|
+
return this._def.args;
|
|
3333
|
+
}
|
|
3334
|
+
returnType() {
|
|
3335
|
+
return this._def.returns;
|
|
3336
|
+
}
|
|
3337
|
+
args(...items) {
|
|
3338
|
+
return new ZodFunction({
|
|
3339
|
+
...this._def,
|
|
3340
|
+
args: ZodTuple.create(items).rest(ZodUnknown.create())
|
|
3341
|
+
});
|
|
3342
|
+
}
|
|
3343
|
+
returns(returnType) {
|
|
3344
|
+
return new ZodFunction({
|
|
3345
|
+
...this._def,
|
|
3346
|
+
returns: returnType
|
|
3347
|
+
});
|
|
3348
|
+
}
|
|
3349
|
+
implement(func) {
|
|
3350
|
+
const validatedFunc = this.parse(func);
|
|
3351
|
+
return validatedFunc;
|
|
3352
|
+
}
|
|
3353
|
+
strictImplement(func) {
|
|
3354
|
+
const validatedFunc = this.parse(func);
|
|
3355
|
+
return validatedFunc;
|
|
3356
|
+
}
|
|
3357
|
+
static create(args, returns, params) {
|
|
3358
|
+
return new ZodFunction({
|
|
3359
|
+
args: args ? args : ZodTuple.create([]).rest(ZodUnknown.create()),
|
|
3360
|
+
returns: returns || ZodUnknown.create(),
|
|
3361
|
+
typeName: ZodFirstPartyTypeKind.ZodFunction,
|
|
3362
|
+
...processCreateParams(params)
|
|
3363
|
+
});
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
|
|
3367
|
+
class ZodLazy extends ZodType {
|
|
3368
|
+
get schema() {
|
|
3369
|
+
return this._def.getter();
|
|
3370
|
+
}
|
|
3371
|
+
_parse(input) {
|
|
3372
|
+
const { ctx } = this._processInputParams(input);
|
|
3373
|
+
const lazySchema = this._def.getter();
|
|
3374
|
+
return lazySchema._parse({ data: ctx.data, path: ctx.path, parent: ctx });
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
ZodLazy.create = (getter, params) => {
|
|
3378
|
+
return new ZodLazy({
|
|
3379
|
+
getter,
|
|
3380
|
+
typeName: ZodFirstPartyTypeKind.ZodLazy,
|
|
3381
|
+
...processCreateParams(params)
|
|
3382
|
+
});
|
|
3383
|
+
};
|
|
3384
|
+
|
|
3385
|
+
class ZodLiteral extends ZodType {
|
|
3386
|
+
_parse(input) {
|
|
3387
|
+
if (input.data !== this._def.value) {
|
|
3388
|
+
const ctx = this._getOrReturnCtx(input);
|
|
3389
|
+
addIssueToContext(ctx, {
|
|
3390
|
+
received: ctx.data,
|
|
3391
|
+
code: ZodIssueCode.invalid_literal,
|
|
3392
|
+
expected: this._def.value
|
|
3393
|
+
});
|
|
3394
|
+
return INVALID;
|
|
3395
|
+
}
|
|
3396
|
+
return { status: "valid", value: input.data };
|
|
3397
|
+
}
|
|
3398
|
+
get value() {
|
|
3399
|
+
return this._def.value;
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
ZodLiteral.create = (value, params) => {
|
|
3403
|
+
return new ZodLiteral({
|
|
3404
|
+
value,
|
|
3405
|
+
typeName: ZodFirstPartyTypeKind.ZodLiteral,
|
|
3406
|
+
...processCreateParams(params)
|
|
3407
|
+
});
|
|
3408
|
+
};
|
|
3409
|
+
function createZodEnum(values, params) {
|
|
3410
|
+
return new ZodEnum({
|
|
3411
|
+
values,
|
|
3412
|
+
typeName: ZodFirstPartyTypeKind.ZodEnum,
|
|
3413
|
+
...processCreateParams(params)
|
|
3414
|
+
});
|
|
3415
|
+
}
|
|
3416
|
+
|
|
3417
|
+
class ZodEnum extends ZodType {
|
|
3418
|
+
_parse(input) {
|
|
3419
|
+
if (typeof input.data !== "string") {
|
|
3420
|
+
const ctx = this._getOrReturnCtx(input);
|
|
3421
|
+
const expectedValues = this._def.values;
|
|
3422
|
+
addIssueToContext(ctx, {
|
|
3423
|
+
expected: util.joinValues(expectedValues),
|
|
3424
|
+
received: ctx.parsedType,
|
|
3425
|
+
code: ZodIssueCode.invalid_type
|
|
3426
|
+
});
|
|
3427
|
+
return INVALID;
|
|
3428
|
+
}
|
|
3429
|
+
if (!this._cache) {
|
|
3430
|
+
this._cache = new Set(this._def.values);
|
|
3431
|
+
}
|
|
3432
|
+
if (!this._cache.has(input.data)) {
|
|
3433
|
+
const ctx = this._getOrReturnCtx(input);
|
|
3434
|
+
const expectedValues = this._def.values;
|
|
3435
|
+
addIssueToContext(ctx, {
|
|
3436
|
+
received: ctx.data,
|
|
3437
|
+
code: ZodIssueCode.invalid_enum_value,
|
|
3438
|
+
options: expectedValues
|
|
3439
|
+
});
|
|
3440
|
+
return INVALID;
|
|
3441
|
+
}
|
|
3442
|
+
return OK(input.data);
|
|
3443
|
+
}
|
|
3444
|
+
get options() {
|
|
3445
|
+
return this._def.values;
|
|
3446
|
+
}
|
|
3447
|
+
get enum() {
|
|
3448
|
+
const enumValues = {};
|
|
3449
|
+
for (const val of this._def.values) {
|
|
3450
|
+
enumValues[val] = val;
|
|
3451
|
+
}
|
|
3452
|
+
return enumValues;
|
|
3453
|
+
}
|
|
3454
|
+
get Values() {
|
|
3455
|
+
const enumValues = {};
|
|
3456
|
+
for (const val of this._def.values) {
|
|
3457
|
+
enumValues[val] = val;
|
|
3458
|
+
}
|
|
3459
|
+
return enumValues;
|
|
3460
|
+
}
|
|
3461
|
+
get Enum() {
|
|
3462
|
+
const enumValues = {};
|
|
3463
|
+
for (const val of this._def.values) {
|
|
3464
|
+
enumValues[val] = val;
|
|
3465
|
+
}
|
|
3466
|
+
return enumValues;
|
|
3467
|
+
}
|
|
3468
|
+
extract(values, newDef = this._def) {
|
|
3469
|
+
return ZodEnum.create(values, {
|
|
3470
|
+
...this._def,
|
|
3471
|
+
...newDef
|
|
3472
|
+
});
|
|
3473
|
+
}
|
|
3474
|
+
exclude(values, newDef = this._def) {
|
|
3475
|
+
return ZodEnum.create(this.options.filter((opt) => !values.includes(opt)), {
|
|
3476
|
+
...this._def,
|
|
3477
|
+
...newDef
|
|
3478
|
+
});
|
|
3479
|
+
}
|
|
3480
|
+
}
|
|
3481
|
+
ZodEnum.create = createZodEnum;
|
|
3482
|
+
|
|
3483
|
+
class ZodNativeEnum extends ZodType {
|
|
3484
|
+
_parse(input) {
|
|
3485
|
+
const nativeEnumValues = util.getValidEnumValues(this._def.values);
|
|
3486
|
+
const ctx = this._getOrReturnCtx(input);
|
|
3487
|
+
if (ctx.parsedType !== ZodParsedType.string && ctx.parsedType !== ZodParsedType.number) {
|
|
3488
|
+
const expectedValues = util.objectValues(nativeEnumValues);
|
|
3489
|
+
addIssueToContext(ctx, {
|
|
3490
|
+
expected: util.joinValues(expectedValues),
|
|
3491
|
+
received: ctx.parsedType,
|
|
3492
|
+
code: ZodIssueCode.invalid_type
|
|
3493
|
+
});
|
|
3494
|
+
return INVALID;
|
|
3495
|
+
}
|
|
3496
|
+
if (!this._cache) {
|
|
3497
|
+
this._cache = new Set(util.getValidEnumValues(this._def.values));
|
|
3498
|
+
}
|
|
3499
|
+
if (!this._cache.has(input.data)) {
|
|
3500
|
+
const expectedValues = util.objectValues(nativeEnumValues);
|
|
3501
|
+
addIssueToContext(ctx, {
|
|
3502
|
+
received: ctx.data,
|
|
3503
|
+
code: ZodIssueCode.invalid_enum_value,
|
|
3504
|
+
options: expectedValues
|
|
3505
|
+
});
|
|
3506
|
+
return INVALID;
|
|
3507
|
+
}
|
|
3508
|
+
return OK(input.data);
|
|
3509
|
+
}
|
|
3510
|
+
get enum() {
|
|
3511
|
+
return this._def.values;
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
ZodNativeEnum.create = (values, params) => {
|
|
3515
|
+
return new ZodNativeEnum({
|
|
3516
|
+
values,
|
|
3517
|
+
typeName: ZodFirstPartyTypeKind.ZodNativeEnum,
|
|
3518
|
+
...processCreateParams(params)
|
|
3519
|
+
});
|
|
3520
|
+
};
|
|
3521
|
+
|
|
3522
|
+
class ZodPromise extends ZodType {
|
|
3523
|
+
unwrap() {
|
|
3524
|
+
return this._def.type;
|
|
3844
3525
|
}
|
|
3845
3526
|
_parse(input) {
|
|
3846
3527
|
const { ctx } = this._processInputParams(input);
|
|
3847
|
-
if (ctx.parsedType !== ZodParsedType.
|
|
3528
|
+
if (ctx.parsedType !== ZodParsedType.promise && ctx.common.async === false) {
|
|
3848
3529
|
addIssueToContext(ctx, {
|
|
3849
3530
|
code: ZodIssueCode.invalid_type,
|
|
3850
|
-
expected: ZodParsedType.
|
|
3531
|
+
expected: ZodParsedType.promise,
|
|
3851
3532
|
received: ctx.parsedType
|
|
3852
3533
|
});
|
|
3853
3534
|
return INVALID;
|
|
3854
3535
|
}
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3536
|
+
const promisified = ctx.parsedType === ZodParsedType.promise ? ctx.data : Promise.resolve(ctx.data);
|
|
3537
|
+
return OK(promisified.then((data) => {
|
|
3538
|
+
return this._def.type.parseAsync(data, {
|
|
3858
3539
|
path: ctx.path,
|
|
3859
|
-
|
|
3860
|
-
issueData: {
|
|
3861
|
-
code: ZodIssueCode.invalid_arguments,
|
|
3862
|
-
argumentsError: error
|
|
3863
|
-
}
|
|
3540
|
+
errorMap: ctx.common.contextualErrorMap
|
|
3864
3541
|
});
|
|
3865
|
-
}
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3542
|
+
}));
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3545
|
+
ZodPromise.create = (schema, params) => {
|
|
3546
|
+
return new ZodPromise({
|
|
3547
|
+
type: schema,
|
|
3548
|
+
typeName: ZodFirstPartyTypeKind.ZodPromise,
|
|
3549
|
+
...processCreateParams(params)
|
|
3550
|
+
});
|
|
3551
|
+
};
|
|
3552
|
+
|
|
3553
|
+
class ZodEffects extends ZodType {
|
|
3554
|
+
innerType() {
|
|
3555
|
+
return this._def.schema;
|
|
3556
|
+
}
|
|
3557
|
+
sourceType() {
|
|
3558
|
+
return this._def.schema._def.typeName === ZodFirstPartyTypeKind.ZodEffects ? this._def.schema.sourceType() : this._def.schema;
|
|
3559
|
+
}
|
|
3560
|
+
_parse(input) {
|
|
3561
|
+
const { status, ctx } = this._processInputParams(input);
|
|
3562
|
+
const effect = this._def.effect || null;
|
|
3563
|
+
const checkCtx = {
|
|
3564
|
+
addIssue: (arg) => {
|
|
3565
|
+
addIssueToContext(ctx, arg);
|
|
3566
|
+
if (arg.fatal) {
|
|
3567
|
+
status.abort();
|
|
3568
|
+
} else {
|
|
3569
|
+
status.dirty();
|
|
3874
3570
|
}
|
|
3875
|
-
}
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3571
|
+
},
|
|
3572
|
+
get path() {
|
|
3573
|
+
return ctx.path;
|
|
3574
|
+
}
|
|
3575
|
+
};
|
|
3576
|
+
checkCtx.addIssue = checkCtx.addIssue.bind(checkCtx);
|
|
3577
|
+
if (effect.type === "preprocess") {
|
|
3578
|
+
const processed = effect.transform(ctx.data, checkCtx);
|
|
3579
|
+
if (ctx.common.async) {
|
|
3580
|
+
return Promise.resolve(processed).then(async (processed2) => {
|
|
3581
|
+
if (status.value === "aborted")
|
|
3582
|
+
return INVALID;
|
|
3583
|
+
const result = await this._def.schema._parseAsync({
|
|
3584
|
+
data: processed2,
|
|
3585
|
+
path: ctx.path,
|
|
3586
|
+
parent: ctx
|
|
3587
|
+
});
|
|
3588
|
+
if (result.status === "aborted")
|
|
3589
|
+
return INVALID;
|
|
3590
|
+
if (result.status === "dirty")
|
|
3591
|
+
return DIRTY(result.value);
|
|
3592
|
+
if (status.value === "dirty")
|
|
3593
|
+
return DIRTY(result.value);
|
|
3594
|
+
return result;
|
|
3886
3595
|
});
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3596
|
+
} else {
|
|
3597
|
+
if (status.value === "aborted")
|
|
3598
|
+
return INVALID;
|
|
3599
|
+
const result = this._def.schema._parseSync({
|
|
3600
|
+
data: processed,
|
|
3601
|
+
path: ctx.path,
|
|
3602
|
+
parent: ctx
|
|
3891
3603
|
});
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3604
|
+
if (result.status === "aborted")
|
|
3605
|
+
return INVALID;
|
|
3606
|
+
if (result.status === "dirty")
|
|
3607
|
+
return DIRTY(result.value);
|
|
3608
|
+
if (status.value === "dirty")
|
|
3609
|
+
return DIRTY(result.value);
|
|
3610
|
+
return result;
|
|
3611
|
+
}
|
|
3612
|
+
}
|
|
3613
|
+
if (effect.type === "refinement") {
|
|
3614
|
+
const executeRefinement = (acc) => {
|
|
3615
|
+
const result = effect.refinement(acc, checkCtx);
|
|
3616
|
+
if (ctx.common.async) {
|
|
3617
|
+
return Promise.resolve(result);
|
|
3900
3618
|
}
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
if (!parsedReturns.success) {
|
|
3904
|
-
throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]);
|
|
3619
|
+
if (result instanceof Promise) {
|
|
3620
|
+
throw new Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead.");
|
|
3905
3621
|
}
|
|
3906
|
-
return
|
|
3907
|
-
}
|
|
3622
|
+
return acc;
|
|
3623
|
+
};
|
|
3624
|
+
if (ctx.common.async === false) {
|
|
3625
|
+
const inner = this._def.schema._parseSync({
|
|
3626
|
+
data: ctx.data,
|
|
3627
|
+
path: ctx.path,
|
|
3628
|
+
parent: ctx
|
|
3629
|
+
});
|
|
3630
|
+
if (inner.status === "aborted")
|
|
3631
|
+
return INVALID;
|
|
3632
|
+
if (inner.status === "dirty")
|
|
3633
|
+
status.dirty();
|
|
3634
|
+
executeRefinement(inner.value);
|
|
3635
|
+
return { status: status.value, value: inner.value };
|
|
3636
|
+
} else {
|
|
3637
|
+
return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((inner) => {
|
|
3638
|
+
if (inner.status === "aborted")
|
|
3639
|
+
return INVALID;
|
|
3640
|
+
if (inner.status === "dirty")
|
|
3641
|
+
status.dirty();
|
|
3642
|
+
return executeRefinement(inner.value).then(() => {
|
|
3643
|
+
return { status: status.value, value: inner.value };
|
|
3644
|
+
});
|
|
3645
|
+
});
|
|
3646
|
+
}
|
|
3908
3647
|
}
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
}
|
|
3936
|
-
static create(args, returns, params) {
|
|
3937
|
-
return new ZodFunction({
|
|
3938
|
-
args: args ? args : ZodTuple.create([]).rest(ZodUnknown.create()),
|
|
3939
|
-
returns: returns || ZodUnknown.create(),
|
|
3940
|
-
typeName: ZodFirstPartyTypeKind.ZodFunction,
|
|
3941
|
-
...processCreateParams(params)
|
|
3942
|
-
});
|
|
3648
|
+
if (effect.type === "transform") {
|
|
3649
|
+
if (ctx.common.async === false) {
|
|
3650
|
+
const base = this._def.schema._parseSync({
|
|
3651
|
+
data: ctx.data,
|
|
3652
|
+
path: ctx.path,
|
|
3653
|
+
parent: ctx
|
|
3654
|
+
});
|
|
3655
|
+
if (!isValid(base))
|
|
3656
|
+
return INVALID;
|
|
3657
|
+
const result = effect.transform(base.value, checkCtx);
|
|
3658
|
+
if (result instanceof Promise) {
|
|
3659
|
+
throw new Error(`Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`);
|
|
3660
|
+
}
|
|
3661
|
+
return { status: status.value, value: result };
|
|
3662
|
+
} else {
|
|
3663
|
+
return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((base) => {
|
|
3664
|
+
if (!isValid(base))
|
|
3665
|
+
return INVALID;
|
|
3666
|
+
return Promise.resolve(effect.transform(base.value, checkCtx)).then((result) => ({
|
|
3667
|
+
status: status.value,
|
|
3668
|
+
value: result
|
|
3669
|
+
}));
|
|
3670
|
+
});
|
|
3671
|
+
}
|
|
3672
|
+
}
|
|
3673
|
+
util.assertNever(effect);
|
|
3943
3674
|
}
|
|
3944
3675
|
}
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3676
|
+
ZodEffects.create = (schema, effect, params) => {
|
|
3677
|
+
return new ZodEffects({
|
|
3678
|
+
schema,
|
|
3679
|
+
typeName: ZodFirstPartyTypeKind.ZodEffects,
|
|
3680
|
+
effect,
|
|
3681
|
+
...processCreateParams(params)
|
|
3682
|
+
});
|
|
3683
|
+
};
|
|
3684
|
+
ZodEffects.createWithPreprocess = (preprocess, schema, params) => {
|
|
3685
|
+
return new ZodEffects({
|
|
3686
|
+
schema,
|
|
3687
|
+
effect: { type: "preprocess", transform: preprocess },
|
|
3688
|
+
typeName: ZodFirstPartyTypeKind.ZodEffects,
|
|
3689
|
+
...processCreateParams(params)
|
|
3690
|
+
});
|
|
3691
|
+
};
|
|
3692
|
+
class ZodOptional extends ZodType {
|
|
3950
3693
|
_parse(input) {
|
|
3951
|
-
const
|
|
3952
|
-
|
|
3953
|
-
|
|
3694
|
+
const parsedType = this._getType(input);
|
|
3695
|
+
if (parsedType === ZodParsedType.undefined) {
|
|
3696
|
+
return OK(undefined);
|
|
3697
|
+
}
|
|
3698
|
+
return this._def.innerType._parse(input);
|
|
3699
|
+
}
|
|
3700
|
+
unwrap() {
|
|
3701
|
+
return this._def.innerType;
|
|
3954
3702
|
}
|
|
3955
3703
|
}
|
|
3956
|
-
|
|
3957
|
-
return new
|
|
3958
|
-
|
|
3959
|
-
typeName: ZodFirstPartyTypeKind.
|
|
3704
|
+
ZodOptional.create = (type, params) => {
|
|
3705
|
+
return new ZodOptional({
|
|
3706
|
+
innerType: type,
|
|
3707
|
+
typeName: ZodFirstPartyTypeKind.ZodOptional,
|
|
3960
3708
|
...processCreateParams(params)
|
|
3961
3709
|
});
|
|
3962
3710
|
};
|
|
3963
3711
|
|
|
3964
|
-
class
|
|
3712
|
+
class ZodNullable extends ZodType {
|
|
3965
3713
|
_parse(input) {
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
received: ctx.data,
|
|
3970
|
-
code: ZodIssueCode.invalid_literal,
|
|
3971
|
-
expected: this._def.value
|
|
3972
|
-
});
|
|
3973
|
-
return INVALID;
|
|
3714
|
+
const parsedType = this._getType(input);
|
|
3715
|
+
if (parsedType === ZodParsedType.null) {
|
|
3716
|
+
return OK(null);
|
|
3974
3717
|
}
|
|
3975
|
-
return
|
|
3718
|
+
return this._def.innerType._parse(input);
|
|
3976
3719
|
}
|
|
3977
|
-
|
|
3978
|
-
return this._def.
|
|
3720
|
+
unwrap() {
|
|
3721
|
+
return this._def.innerType;
|
|
3979
3722
|
}
|
|
3980
3723
|
}
|
|
3981
|
-
|
|
3982
|
-
return new
|
|
3983
|
-
|
|
3984
|
-
typeName: ZodFirstPartyTypeKind.
|
|
3724
|
+
ZodNullable.create = (type, params) => {
|
|
3725
|
+
return new ZodNullable({
|
|
3726
|
+
innerType: type,
|
|
3727
|
+
typeName: ZodFirstPartyTypeKind.ZodNullable,
|
|
3985
3728
|
...processCreateParams(params)
|
|
3986
3729
|
});
|
|
3987
3730
|
};
|
|
3988
|
-
function createZodEnum(values, params) {
|
|
3989
|
-
return new ZodEnum({
|
|
3990
|
-
values,
|
|
3991
|
-
typeName: ZodFirstPartyTypeKind.ZodEnum,
|
|
3992
|
-
...processCreateParams(params)
|
|
3993
|
-
});
|
|
3994
|
-
}
|
|
3995
3731
|
|
|
3996
|
-
class
|
|
3732
|
+
class ZodDefault extends ZodType {
|
|
3997
3733
|
_parse(input) {
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
expected: util.joinValues(expectedValues),
|
|
4003
|
-
received: ctx.parsedType,
|
|
4004
|
-
code: ZodIssueCode.invalid_type
|
|
4005
|
-
});
|
|
4006
|
-
return INVALID;
|
|
4007
|
-
}
|
|
4008
|
-
if (!this._cache) {
|
|
4009
|
-
this._cache = new Set(this._def.values);
|
|
4010
|
-
}
|
|
4011
|
-
if (!this._cache.has(input.data)) {
|
|
4012
|
-
const ctx = this._getOrReturnCtx(input);
|
|
4013
|
-
const expectedValues = this._def.values;
|
|
4014
|
-
addIssueToContext(ctx, {
|
|
4015
|
-
received: ctx.data,
|
|
4016
|
-
code: ZodIssueCode.invalid_enum_value,
|
|
4017
|
-
options: expectedValues
|
|
4018
|
-
});
|
|
4019
|
-
return INVALID;
|
|
4020
|
-
}
|
|
4021
|
-
return OK(input.data);
|
|
4022
|
-
}
|
|
4023
|
-
get options() {
|
|
4024
|
-
return this._def.values;
|
|
4025
|
-
}
|
|
4026
|
-
get enum() {
|
|
4027
|
-
const enumValues = {};
|
|
4028
|
-
for (const val of this._def.values) {
|
|
4029
|
-
enumValues[val] = val;
|
|
4030
|
-
}
|
|
4031
|
-
return enumValues;
|
|
4032
|
-
}
|
|
4033
|
-
get Values() {
|
|
4034
|
-
const enumValues = {};
|
|
4035
|
-
for (const val of this._def.values) {
|
|
4036
|
-
enumValues[val] = val;
|
|
4037
|
-
}
|
|
4038
|
-
return enumValues;
|
|
4039
|
-
}
|
|
4040
|
-
get Enum() {
|
|
4041
|
-
const enumValues = {};
|
|
4042
|
-
for (const val of this._def.values) {
|
|
4043
|
-
enumValues[val] = val;
|
|
3734
|
+
const { ctx } = this._processInputParams(input);
|
|
3735
|
+
let data = ctx.data;
|
|
3736
|
+
if (ctx.parsedType === ZodParsedType.undefined) {
|
|
3737
|
+
data = this._def.defaultValue();
|
|
4044
3738
|
}
|
|
4045
|
-
return
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
...this._def,
|
|
4050
|
-
...newDef
|
|
3739
|
+
return this._def.innerType._parse({
|
|
3740
|
+
data,
|
|
3741
|
+
path: ctx.path,
|
|
3742
|
+
parent: ctx
|
|
4051
3743
|
});
|
|
4052
3744
|
}
|
|
4053
|
-
|
|
4054
|
-
return
|
|
4055
|
-
...this._def,
|
|
4056
|
-
...newDef
|
|
4057
|
-
});
|
|
3745
|
+
removeDefault() {
|
|
3746
|
+
return this._def.innerType;
|
|
4058
3747
|
}
|
|
4059
3748
|
}
|
|
4060
|
-
|
|
3749
|
+
ZodDefault.create = (type, params) => {
|
|
3750
|
+
return new ZodDefault({
|
|
3751
|
+
innerType: type,
|
|
3752
|
+
typeName: ZodFirstPartyTypeKind.ZodDefault,
|
|
3753
|
+
defaultValue: typeof params.default === "function" ? params.default : () => params.default,
|
|
3754
|
+
...processCreateParams(params)
|
|
3755
|
+
});
|
|
3756
|
+
};
|
|
4061
3757
|
|
|
4062
|
-
class
|
|
3758
|
+
class ZodCatch extends ZodType {
|
|
4063
3759
|
_parse(input) {
|
|
4064
|
-
const
|
|
4065
|
-
const
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
3760
|
+
const { ctx } = this._processInputParams(input);
|
|
3761
|
+
const newCtx = {
|
|
3762
|
+
...ctx,
|
|
3763
|
+
common: {
|
|
3764
|
+
...ctx.common,
|
|
3765
|
+
issues: []
|
|
3766
|
+
}
|
|
3767
|
+
};
|
|
3768
|
+
const result = this._def.innerType._parse({
|
|
3769
|
+
data: newCtx.data,
|
|
3770
|
+
path: newCtx.path,
|
|
3771
|
+
parent: {
|
|
3772
|
+
...newCtx
|
|
3773
|
+
}
|
|
3774
|
+
});
|
|
3775
|
+
if (isAsync(result)) {
|
|
3776
|
+
return result.then((result2) => {
|
|
3777
|
+
return {
|
|
3778
|
+
status: "valid",
|
|
3779
|
+
value: result2.status === "valid" ? result2.value : this._def.catchValue({
|
|
3780
|
+
get error() {
|
|
3781
|
+
return new ZodError(newCtx.common.issues);
|
|
3782
|
+
},
|
|
3783
|
+
input: newCtx.data
|
|
3784
|
+
})
|
|
3785
|
+
};
|
|
4084
3786
|
});
|
|
4085
|
-
|
|
3787
|
+
} else {
|
|
3788
|
+
return {
|
|
3789
|
+
status: "valid",
|
|
3790
|
+
value: result.status === "valid" ? result.value : this._def.catchValue({
|
|
3791
|
+
get error() {
|
|
3792
|
+
return new ZodError(newCtx.common.issues);
|
|
3793
|
+
},
|
|
3794
|
+
input: newCtx.data
|
|
3795
|
+
})
|
|
3796
|
+
};
|
|
4086
3797
|
}
|
|
4087
|
-
return OK(input.data);
|
|
4088
3798
|
}
|
|
4089
|
-
|
|
4090
|
-
return this._def.
|
|
3799
|
+
removeCatch() {
|
|
3800
|
+
return this._def.innerType;
|
|
4091
3801
|
}
|
|
4092
3802
|
}
|
|
4093
|
-
|
|
4094
|
-
return new
|
|
4095
|
-
|
|
4096
|
-
typeName: ZodFirstPartyTypeKind.
|
|
3803
|
+
ZodCatch.create = (type, params) => {
|
|
3804
|
+
return new ZodCatch({
|
|
3805
|
+
innerType: type,
|
|
3806
|
+
typeName: ZodFirstPartyTypeKind.ZodCatch,
|
|
3807
|
+
catchValue: typeof params.catch === "function" ? params.catch : () => params.catch,
|
|
4097
3808
|
...processCreateParams(params)
|
|
4098
3809
|
});
|
|
4099
3810
|
};
|
|
4100
3811
|
|
|
4101
|
-
class
|
|
4102
|
-
unwrap() {
|
|
4103
|
-
return this._def.type;
|
|
4104
|
-
}
|
|
3812
|
+
class ZodNaN extends ZodType {
|
|
4105
3813
|
_parse(input) {
|
|
4106
|
-
const
|
|
4107
|
-
if (
|
|
3814
|
+
const parsedType = this._getType(input);
|
|
3815
|
+
if (parsedType !== ZodParsedType.nan) {
|
|
3816
|
+
const ctx = this._getOrReturnCtx(input);
|
|
4108
3817
|
addIssueToContext(ctx, {
|
|
4109
3818
|
code: ZodIssueCode.invalid_type,
|
|
4110
|
-
expected: ZodParsedType.
|
|
3819
|
+
expected: ZodParsedType.nan,
|
|
4111
3820
|
received: ctx.parsedType
|
|
4112
3821
|
});
|
|
4113
3822
|
return INVALID;
|
|
4114
3823
|
}
|
|
4115
|
-
|
|
4116
|
-
return OK(promisified.then((data) => {
|
|
4117
|
-
return this._def.type.parseAsync(data, {
|
|
4118
|
-
path: ctx.path,
|
|
4119
|
-
errorMap: ctx.common.contextualErrorMap
|
|
4120
|
-
});
|
|
4121
|
-
}));
|
|
3824
|
+
return { status: "valid", value: input.data };
|
|
4122
3825
|
}
|
|
4123
3826
|
}
|
|
4124
|
-
|
|
4125
|
-
return new
|
|
4126
|
-
|
|
4127
|
-
typeName: ZodFirstPartyTypeKind.ZodPromise,
|
|
3827
|
+
ZodNaN.create = (params) => {
|
|
3828
|
+
return new ZodNaN({
|
|
3829
|
+
typeName: ZodFirstPartyTypeKind.ZodNaN,
|
|
4128
3830
|
...processCreateParams(params)
|
|
4129
3831
|
});
|
|
4130
3832
|
};
|
|
3833
|
+
var BRAND = Symbol("zod_brand");
|
|
4131
3834
|
|
|
4132
|
-
class
|
|
4133
|
-
|
|
4134
|
-
|
|
3835
|
+
class ZodBranded extends ZodType {
|
|
3836
|
+
_parse(input) {
|
|
3837
|
+
const { ctx } = this._processInputParams(input);
|
|
3838
|
+
const data = ctx.data;
|
|
3839
|
+
return this._def.type._parse({
|
|
3840
|
+
data,
|
|
3841
|
+
path: ctx.path,
|
|
3842
|
+
parent: ctx
|
|
3843
|
+
});
|
|
4135
3844
|
}
|
|
4136
|
-
|
|
4137
|
-
return this._def.
|
|
3845
|
+
unwrap() {
|
|
3846
|
+
return this._def.type;
|
|
4138
3847
|
}
|
|
3848
|
+
}
|
|
3849
|
+
|
|
3850
|
+
class ZodPipeline extends ZodType {
|
|
4139
3851
|
_parse(input) {
|
|
4140
3852
|
const { status, ctx } = this._processInputParams(input);
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
}
|
|
3853
|
+
if (ctx.common.async) {
|
|
3854
|
+
const handleAsync = async () => {
|
|
3855
|
+
const inResult = await this._def.in._parseAsync({
|
|
3856
|
+
data: ctx.data,
|
|
3857
|
+
path: ctx.path,
|
|
3858
|
+
parent: ctx
|
|
3859
|
+
});
|
|
3860
|
+
if (inResult.status === "aborted")
|
|
3861
|
+
return INVALID;
|
|
3862
|
+
if (inResult.status === "dirty") {
|
|
4148
3863
|
status.dirty();
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
}
|
|
4154
|
-
};
|
|
4155
|
-
checkCtx.addIssue = checkCtx.addIssue.bind(checkCtx);
|
|
4156
|
-
if (effect.type === "preprocess") {
|
|
4157
|
-
const processed = effect.transform(ctx.data, checkCtx);
|
|
4158
|
-
if (ctx.common.async) {
|
|
4159
|
-
return Promise.resolve(processed).then(async (processed2) => {
|
|
4160
|
-
if (status.value === "aborted")
|
|
4161
|
-
return INVALID;
|
|
4162
|
-
const result = await this._def.schema._parseAsync({
|
|
4163
|
-
data: processed2,
|
|
3864
|
+
return DIRTY(inResult.value);
|
|
3865
|
+
} else {
|
|
3866
|
+
return this._def.out._parseAsync({
|
|
3867
|
+
data: inResult.value,
|
|
4164
3868
|
path: ctx.path,
|
|
4165
3869
|
parent: ctx
|
|
4166
3870
|
});
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
3871
|
+
}
|
|
3872
|
+
};
|
|
3873
|
+
return handleAsync();
|
|
3874
|
+
} else {
|
|
3875
|
+
const inResult = this._def.in._parseSync({
|
|
3876
|
+
data: ctx.data,
|
|
3877
|
+
path: ctx.path,
|
|
3878
|
+
parent: ctx
|
|
3879
|
+
});
|
|
3880
|
+
if (inResult.status === "aborted")
|
|
3881
|
+
return INVALID;
|
|
3882
|
+
if (inResult.status === "dirty") {
|
|
3883
|
+
status.dirty();
|
|
3884
|
+
return {
|
|
3885
|
+
status: "dirty",
|
|
3886
|
+
value: inResult.value
|
|
3887
|
+
};
|
|
4175
3888
|
} else {
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
const result = this._def.schema._parseSync({
|
|
4179
|
-
data: processed,
|
|
3889
|
+
return this._def.out._parseSync({
|
|
3890
|
+
data: inResult.value,
|
|
4180
3891
|
path: ctx.path,
|
|
4181
3892
|
parent: ctx
|
|
4182
3893
|
});
|
|
4183
|
-
if (result.status === "aborted")
|
|
4184
|
-
return INVALID;
|
|
4185
|
-
if (result.status === "dirty")
|
|
4186
|
-
return DIRTY(result.value);
|
|
4187
|
-
if (status.value === "dirty")
|
|
4188
|
-
return DIRTY(result.value);
|
|
4189
|
-
return result;
|
|
4190
3894
|
}
|
|
4191
3895
|
}
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
3896
|
+
}
|
|
3897
|
+
static create(a, b) {
|
|
3898
|
+
return new ZodPipeline({
|
|
3899
|
+
in: a,
|
|
3900
|
+
out: b,
|
|
3901
|
+
typeName: ZodFirstPartyTypeKind.ZodPipeline
|
|
3902
|
+
});
|
|
3903
|
+
}
|
|
3904
|
+
}
|
|
3905
|
+
|
|
3906
|
+
class ZodReadonly extends ZodType {
|
|
3907
|
+
_parse(input) {
|
|
3908
|
+
const result = this._def.innerType._parse(input);
|
|
3909
|
+
const freeze = (data) => {
|
|
3910
|
+
if (isValid(data)) {
|
|
3911
|
+
data.value = Object.freeze(data.value);
|
|
3912
|
+
}
|
|
3913
|
+
return data;
|
|
3914
|
+
};
|
|
3915
|
+
return isAsync(result) ? result.then((data) => freeze(data)) : freeze(result);
|
|
3916
|
+
}
|
|
3917
|
+
unwrap() {
|
|
3918
|
+
return this._def.innerType;
|
|
3919
|
+
}
|
|
3920
|
+
}
|
|
3921
|
+
ZodReadonly.create = (type, params) => {
|
|
3922
|
+
return new ZodReadonly({
|
|
3923
|
+
innerType: type,
|
|
3924
|
+
typeName: ZodFirstPartyTypeKind.ZodReadonly,
|
|
3925
|
+
...processCreateParams(params)
|
|
3926
|
+
});
|
|
3927
|
+
};
|
|
3928
|
+
function cleanParams(params, data) {
|
|
3929
|
+
const p = typeof params === "function" ? params(data) : typeof params === "string" ? { message: params } : params;
|
|
3930
|
+
const p2 = typeof p === "string" ? { message: p } : p;
|
|
3931
|
+
return p2;
|
|
3932
|
+
}
|
|
3933
|
+
function custom(check, _params = {}, fatal) {
|
|
3934
|
+
if (check)
|
|
3935
|
+
return ZodAny.create().superRefine((data, ctx) => {
|
|
3936
|
+
const r = check(data);
|
|
3937
|
+
if (r instanceof Promise) {
|
|
3938
|
+
return r.then((r2) => {
|
|
3939
|
+
if (!r2) {
|
|
3940
|
+
const params = cleanParams(_params, data);
|
|
3941
|
+
const _fatal = params.fatal ?? fatal ?? true;
|
|
3942
|
+
ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
|
|
3943
|
+
}
|
|
4224
3944
|
});
|
|
4225
3945
|
}
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
3946
|
+
if (!r) {
|
|
3947
|
+
const params = cleanParams(_params, data);
|
|
3948
|
+
const _fatal = params.fatal ?? fatal ?? true;
|
|
3949
|
+
ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
|
|
3950
|
+
}
|
|
3951
|
+
return;
|
|
3952
|
+
});
|
|
3953
|
+
return ZodAny.create();
|
|
3954
|
+
}
|
|
3955
|
+
var late = {
|
|
3956
|
+
object: ZodObject.lazycreate
|
|
3957
|
+
};
|
|
3958
|
+
var ZodFirstPartyTypeKind;
|
|
3959
|
+
(function(ZodFirstPartyTypeKind2) {
|
|
3960
|
+
ZodFirstPartyTypeKind2["ZodString"] = "ZodString";
|
|
3961
|
+
ZodFirstPartyTypeKind2["ZodNumber"] = "ZodNumber";
|
|
3962
|
+
ZodFirstPartyTypeKind2["ZodNaN"] = "ZodNaN";
|
|
3963
|
+
ZodFirstPartyTypeKind2["ZodBigInt"] = "ZodBigInt";
|
|
3964
|
+
ZodFirstPartyTypeKind2["ZodBoolean"] = "ZodBoolean";
|
|
3965
|
+
ZodFirstPartyTypeKind2["ZodDate"] = "ZodDate";
|
|
3966
|
+
ZodFirstPartyTypeKind2["ZodSymbol"] = "ZodSymbol";
|
|
3967
|
+
ZodFirstPartyTypeKind2["ZodUndefined"] = "ZodUndefined";
|
|
3968
|
+
ZodFirstPartyTypeKind2["ZodNull"] = "ZodNull";
|
|
3969
|
+
ZodFirstPartyTypeKind2["ZodAny"] = "ZodAny";
|
|
3970
|
+
ZodFirstPartyTypeKind2["ZodUnknown"] = "ZodUnknown";
|
|
3971
|
+
ZodFirstPartyTypeKind2["ZodNever"] = "ZodNever";
|
|
3972
|
+
ZodFirstPartyTypeKind2["ZodVoid"] = "ZodVoid";
|
|
3973
|
+
ZodFirstPartyTypeKind2["ZodArray"] = "ZodArray";
|
|
3974
|
+
ZodFirstPartyTypeKind2["ZodObject"] = "ZodObject";
|
|
3975
|
+
ZodFirstPartyTypeKind2["ZodUnion"] = "ZodUnion";
|
|
3976
|
+
ZodFirstPartyTypeKind2["ZodDiscriminatedUnion"] = "ZodDiscriminatedUnion";
|
|
3977
|
+
ZodFirstPartyTypeKind2["ZodIntersection"] = "ZodIntersection";
|
|
3978
|
+
ZodFirstPartyTypeKind2["ZodTuple"] = "ZodTuple";
|
|
3979
|
+
ZodFirstPartyTypeKind2["ZodRecord"] = "ZodRecord";
|
|
3980
|
+
ZodFirstPartyTypeKind2["ZodMap"] = "ZodMap";
|
|
3981
|
+
ZodFirstPartyTypeKind2["ZodSet"] = "ZodSet";
|
|
3982
|
+
ZodFirstPartyTypeKind2["ZodFunction"] = "ZodFunction";
|
|
3983
|
+
ZodFirstPartyTypeKind2["ZodLazy"] = "ZodLazy";
|
|
3984
|
+
ZodFirstPartyTypeKind2["ZodLiteral"] = "ZodLiteral";
|
|
3985
|
+
ZodFirstPartyTypeKind2["ZodEnum"] = "ZodEnum";
|
|
3986
|
+
ZodFirstPartyTypeKind2["ZodEffects"] = "ZodEffects";
|
|
3987
|
+
ZodFirstPartyTypeKind2["ZodNativeEnum"] = "ZodNativeEnum";
|
|
3988
|
+
ZodFirstPartyTypeKind2["ZodOptional"] = "ZodOptional";
|
|
3989
|
+
ZodFirstPartyTypeKind2["ZodNullable"] = "ZodNullable";
|
|
3990
|
+
ZodFirstPartyTypeKind2["ZodDefault"] = "ZodDefault";
|
|
3991
|
+
ZodFirstPartyTypeKind2["ZodCatch"] = "ZodCatch";
|
|
3992
|
+
ZodFirstPartyTypeKind2["ZodPromise"] = "ZodPromise";
|
|
3993
|
+
ZodFirstPartyTypeKind2["ZodBranded"] = "ZodBranded";
|
|
3994
|
+
ZodFirstPartyTypeKind2["ZodPipeline"] = "ZodPipeline";
|
|
3995
|
+
ZodFirstPartyTypeKind2["ZodReadonly"] = "ZodReadonly";
|
|
3996
|
+
})(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));
|
|
3997
|
+
var instanceOfType = (cls, params = {
|
|
3998
|
+
message: `Input not instance of ${cls.name}`
|
|
3999
|
+
}) => custom((data) => data instanceof cls, params);
|
|
4000
|
+
var stringType = ZodString.create;
|
|
4001
|
+
var numberType = ZodNumber.create;
|
|
4002
|
+
var nanType = ZodNaN.create;
|
|
4003
|
+
var bigIntType = ZodBigInt.create;
|
|
4004
|
+
var booleanType = ZodBoolean.create;
|
|
4005
|
+
var dateType = ZodDate.create;
|
|
4006
|
+
var symbolType = ZodSymbol.create;
|
|
4007
|
+
var undefinedType = ZodUndefined.create;
|
|
4008
|
+
var nullType = ZodNull.create;
|
|
4009
|
+
var anyType = ZodAny.create;
|
|
4010
|
+
var unknownType = ZodUnknown.create;
|
|
4011
|
+
var neverType = ZodNever.create;
|
|
4012
|
+
var voidType = ZodVoid.create;
|
|
4013
|
+
var arrayType = ZodArray.create;
|
|
4014
|
+
var objectType = ZodObject.create;
|
|
4015
|
+
var strictObjectType = ZodObject.strictCreate;
|
|
4016
|
+
var unionType = ZodUnion.create;
|
|
4017
|
+
var discriminatedUnionType = ZodDiscriminatedUnion.create;
|
|
4018
|
+
var intersectionType = ZodIntersection.create;
|
|
4019
|
+
var tupleType = ZodTuple.create;
|
|
4020
|
+
var recordType = ZodRecord.create;
|
|
4021
|
+
var mapType = ZodMap.create;
|
|
4022
|
+
var setType = ZodSet.create;
|
|
4023
|
+
var functionType = ZodFunction.create;
|
|
4024
|
+
var lazyType = ZodLazy.create;
|
|
4025
|
+
var literalType = ZodLiteral.create;
|
|
4026
|
+
var enumType = ZodEnum.create;
|
|
4027
|
+
var nativeEnumType = ZodNativeEnum.create;
|
|
4028
|
+
var promiseType = ZodPromise.create;
|
|
4029
|
+
var effectsType = ZodEffects.create;
|
|
4030
|
+
var optionalType = ZodOptional.create;
|
|
4031
|
+
var nullableType = ZodNullable.create;
|
|
4032
|
+
var preprocessType = ZodEffects.createWithPreprocess;
|
|
4033
|
+
var pipelineType = ZodPipeline.create;
|
|
4034
|
+
var ostring = () => stringType().optional();
|
|
4035
|
+
var onumber = () => numberType().optional();
|
|
4036
|
+
var oboolean = () => booleanType().optional();
|
|
4037
|
+
var coerce = {
|
|
4038
|
+
string: (arg) => ZodString.create({ ...arg, coerce: true }),
|
|
4039
|
+
number: (arg) => ZodNumber.create({ ...arg, coerce: true }),
|
|
4040
|
+
boolean: (arg) => ZodBoolean.create({
|
|
4041
|
+
...arg,
|
|
4042
|
+
coerce: true
|
|
4043
|
+
}),
|
|
4044
|
+
bigint: (arg) => ZodBigInt.create({ ...arg, coerce: true }),
|
|
4045
|
+
date: (arg) => ZodDate.create({ ...arg, coerce: true })
|
|
4046
|
+
};
|
|
4047
|
+
var NEVER = INVALID;
|
|
4048
|
+
// src/types.ts
|
|
4049
|
+
var asZodObject = (s) => s;
|
|
4050
|
+
|
|
4051
|
+
// src/schema.ts
|
|
4052
|
+
function parseRelationsConfig(relations, schemas) {
|
|
4053
|
+
const relationships = [];
|
|
4054
|
+
const added = new Set;
|
|
4055
|
+
for (const [fromTable, rels] of Object.entries(relations)) {
|
|
4056
|
+
if (!schemas[fromTable]) {
|
|
4057
|
+
throw new Error(`relations: unknown table '${fromTable}'`);
|
|
4058
|
+
}
|
|
4059
|
+
for (const [fkColumn, toTable] of Object.entries(rels)) {
|
|
4060
|
+
if (!schemas[toTable]) {
|
|
4061
|
+
throw new Error(`relations: unknown target table '${toTable}' in ${fromTable}.${fkColumn}`);
|
|
4062
|
+
}
|
|
4063
|
+
const navField = fkColumn.replace(/_id$/, "");
|
|
4064
|
+
const btKey = `${fromTable}.${fkColumn}:belongs-to`;
|
|
4065
|
+
if (!added.has(btKey)) {
|
|
4066
|
+
relationships.push({
|
|
4067
|
+
type: "belongs-to",
|
|
4068
|
+
from: fromTable,
|
|
4069
|
+
to: toTable,
|
|
4070
|
+
relationshipField: navField,
|
|
4071
|
+
foreignKey: fkColumn
|
|
4233
4072
|
});
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
return INVALID;
|
|
4245
|
-
return Promise.resolve(effect.transform(base.value, checkCtx)).then((result) => ({
|
|
4246
|
-
status: status.value,
|
|
4247
|
-
value: result
|
|
4248
|
-
}));
|
|
4073
|
+
added.add(btKey);
|
|
4074
|
+
}
|
|
4075
|
+
const otmKey = `${toTable}.${fromTable}:one-to-many`;
|
|
4076
|
+
if (!added.has(otmKey)) {
|
|
4077
|
+
relationships.push({
|
|
4078
|
+
type: "one-to-many",
|
|
4079
|
+
from: toTable,
|
|
4080
|
+
to: fromTable,
|
|
4081
|
+
relationshipField: fromTable,
|
|
4082
|
+
foreignKey: ""
|
|
4249
4083
|
});
|
|
4084
|
+
added.add(otmKey);
|
|
4250
4085
|
}
|
|
4251
4086
|
}
|
|
4252
|
-
util.assertNever(effect);
|
|
4253
4087
|
}
|
|
4088
|
+
return relationships;
|
|
4254
4089
|
}
|
|
4255
|
-
|
|
4256
|
-
return
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
});
|
|
4262
|
-
};
|
|
4263
|
-
ZodEffects.createWithPreprocess = (preprocess, schema, params) => {
|
|
4264
|
-
return new ZodEffects({
|
|
4265
|
-
schema,
|
|
4266
|
-
effect: { type: "preprocess", transform: preprocess },
|
|
4267
|
-
typeName: ZodFirstPartyTypeKind.ZodEffects,
|
|
4268
|
-
...processCreateParams(params)
|
|
4269
|
-
});
|
|
4270
|
-
};
|
|
4271
|
-
class ZodOptional extends ZodType {
|
|
4272
|
-
_parse(input) {
|
|
4273
|
-
const parsedType = this._getType(input);
|
|
4274
|
-
if (parsedType === ZodParsedType.undefined) {
|
|
4275
|
-
return OK(undefined);
|
|
4276
|
-
}
|
|
4277
|
-
return this._def.innerType._parse(input);
|
|
4090
|
+
function getStorableFields(schema) {
|
|
4091
|
+
return Object.entries(asZodObject(schema).shape).filter(([key]) => key !== "id").map(([name, type]) => ({ name, type }));
|
|
4092
|
+
}
|
|
4093
|
+
function zodTypeToSqlType(zodType) {
|
|
4094
|
+
if (zodType instanceof exports_external.ZodOptional) {
|
|
4095
|
+
zodType = zodType._def.innerType;
|
|
4278
4096
|
}
|
|
4279
|
-
|
|
4280
|
-
|
|
4097
|
+
if (zodType instanceof exports_external.ZodDefault) {
|
|
4098
|
+
zodType = zodType._def.innerType;
|
|
4281
4099
|
}
|
|
4100
|
+
if (zodType instanceof exports_external.ZodString || zodType instanceof exports_external.ZodDate)
|
|
4101
|
+
return "TEXT";
|
|
4102
|
+
if (zodType instanceof exports_external.ZodNumber || zodType instanceof exports_external.ZodBoolean)
|
|
4103
|
+
return "INTEGER";
|
|
4104
|
+
if (zodType._def.typeName === "ZodInstanceOf" && zodType._def.type === Buffer)
|
|
4105
|
+
return "BLOB";
|
|
4106
|
+
return "TEXT";
|
|
4282
4107
|
}
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
_parse(input) {
|
|
4293
|
-
const parsedType = this._getType(input);
|
|
4294
|
-
if (parsedType === ZodParsedType.null) {
|
|
4295
|
-
return OK(null);
|
|
4108
|
+
function transformForStorage(data) {
|
|
4109
|
+
const transformed = {};
|
|
4110
|
+
for (const [key, value] of Object.entries(data)) {
|
|
4111
|
+
if (value instanceof Date) {
|
|
4112
|
+
transformed[key] = value.toISOString();
|
|
4113
|
+
} else if (typeof value === "boolean") {
|
|
4114
|
+
transformed[key] = value ? 1 : 0;
|
|
4115
|
+
} else {
|
|
4116
|
+
transformed[key] = value;
|
|
4296
4117
|
}
|
|
4297
|
-
return this._def.innerType._parse(input);
|
|
4298
4118
|
}
|
|
4299
|
-
|
|
4300
|
-
|
|
4119
|
+
return transformed;
|
|
4120
|
+
}
|
|
4121
|
+
function transformFromStorage(row, schema) {
|
|
4122
|
+
const transformed = {};
|
|
4123
|
+
for (const [key, value] of Object.entries(row)) {
|
|
4124
|
+
let fieldSchema = asZodObject(schema).shape[key];
|
|
4125
|
+
if (fieldSchema instanceof exports_external.ZodOptional) {
|
|
4126
|
+
fieldSchema = fieldSchema._def.innerType;
|
|
4127
|
+
}
|
|
4128
|
+
if (fieldSchema instanceof exports_external.ZodDefault) {
|
|
4129
|
+
fieldSchema = fieldSchema._def.innerType;
|
|
4130
|
+
}
|
|
4131
|
+
if (fieldSchema instanceof exports_external.ZodDate && typeof value === "string") {
|
|
4132
|
+
transformed[key] = new Date(value);
|
|
4133
|
+
} else if (fieldSchema instanceof exports_external.ZodBoolean && typeof value === "number") {
|
|
4134
|
+
transformed[key] = value === 1;
|
|
4135
|
+
} else {
|
|
4136
|
+
transformed[key] = value;
|
|
4137
|
+
}
|
|
4301
4138
|
}
|
|
4139
|
+
return transformed;
|
|
4302
4140
|
}
|
|
4303
|
-
ZodNullable.create = (type, params) => {
|
|
4304
|
-
return new ZodNullable({
|
|
4305
|
-
innerType: type,
|
|
4306
|
-
typeName: ZodFirstPartyTypeKind.ZodNullable,
|
|
4307
|
-
...processCreateParams(params)
|
|
4308
|
-
});
|
|
4309
|
-
};
|
|
4310
4141
|
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4142
|
+
// src/query.ts
|
|
4143
|
+
var OPERATOR_MAP = {
|
|
4144
|
+
$gt: ">",
|
|
4145
|
+
$gte: ">=",
|
|
4146
|
+
$lt: "<",
|
|
4147
|
+
$lte: "<=",
|
|
4148
|
+
$ne: "!=",
|
|
4149
|
+
$in: "IN",
|
|
4150
|
+
$like: "LIKE",
|
|
4151
|
+
$notIn: "NOT IN",
|
|
4152
|
+
$between: "BETWEEN"
|
|
4153
|
+
};
|
|
4154
|
+
function transformValueForStorage(value) {
|
|
4155
|
+
if (value instanceof Date)
|
|
4156
|
+
return value.toISOString();
|
|
4157
|
+
if (typeof value === "boolean")
|
|
4158
|
+
return value ? 1 : 0;
|
|
4159
|
+
return value;
|
|
4160
|
+
}
|
|
4161
|
+
function compileIQO(tableName, iqo) {
|
|
4162
|
+
const params = [];
|
|
4163
|
+
const selectParts = [];
|
|
4164
|
+
if (iqo.selects.length > 0) {
|
|
4165
|
+
selectParts.push(...iqo.selects.map((s) => `${tableName}.${s}`));
|
|
4166
|
+
} else {
|
|
4167
|
+
selectParts.push(`${tableName}.*`);
|
|
4168
|
+
}
|
|
4169
|
+
for (const j of iqo.joins) {
|
|
4170
|
+
if (j.columns.length > 0) {
|
|
4171
|
+
selectParts.push(...j.columns.map((c) => `${j.table}.${c} AS ${j.table}_${c}`));
|
|
4172
|
+
} else {
|
|
4173
|
+
selectParts.push(`${j.table}.*`);
|
|
4317
4174
|
}
|
|
4318
|
-
return this._def.innerType._parse({
|
|
4319
|
-
data,
|
|
4320
|
-
path: ctx.path,
|
|
4321
|
-
parent: ctx
|
|
4322
|
-
});
|
|
4323
4175
|
}
|
|
4324
|
-
|
|
4325
|
-
|
|
4176
|
+
let sql = `SELECT ${selectParts.join(", ")} FROM ${tableName}`;
|
|
4177
|
+
for (const j of iqo.joins) {
|
|
4178
|
+
sql += ` JOIN ${j.table} ON ${tableName}.${j.fromCol} = ${j.table}.${j.toCol}`;
|
|
4326
4179
|
}
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
}
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
...
|
|
4180
|
+
if (iqo.whereAST) {
|
|
4181
|
+
const compiled = compileAST(iqo.whereAST);
|
|
4182
|
+
sql += ` WHERE ${compiled.sql}`;
|
|
4183
|
+
params.push(...compiled.params);
|
|
4184
|
+
} else if (iqo.wheres.length > 0) {
|
|
4185
|
+
const hasJoins = iqo.joins.length > 0;
|
|
4186
|
+
const qualify = (field) => hasJoins && !field.includes(".") ? `${tableName}.${field}` : field;
|
|
4187
|
+
const whereParts = [];
|
|
4188
|
+
for (const w of iqo.wheres) {
|
|
4189
|
+
if (w.operator === "IN") {
|
|
4190
|
+
const arr = w.value;
|
|
4191
|
+
if (arr.length === 0) {
|
|
4192
|
+
whereParts.push("1 = 0");
|
|
4193
|
+
} else {
|
|
4194
|
+
const placeholders = arr.map(() => "?").join(", ");
|
|
4195
|
+
whereParts.push(`${qualify(w.field)} IN (${placeholders})`);
|
|
4196
|
+
params.push(...arr.map(transformValueForStorage));
|
|
4197
|
+
}
|
|
4198
|
+
} else if (w.operator === "NOT IN") {
|
|
4199
|
+
const arr = w.value;
|
|
4200
|
+
if (arr.length === 0)
|
|
4201
|
+
continue;
|
|
4202
|
+
const placeholders = arr.map(() => "?").join(", ");
|
|
4203
|
+
whereParts.push(`${qualify(w.field)} NOT IN (${placeholders})`);
|
|
4204
|
+
params.push(...arr.map(transformValueForStorage));
|
|
4205
|
+
} else if (w.operator === "BETWEEN") {
|
|
4206
|
+
const [min, max] = w.value;
|
|
4207
|
+
whereParts.push(`${qualify(w.field)} BETWEEN ? AND ?`);
|
|
4208
|
+
params.push(transformValueForStorage(min), transformValueForStorage(max));
|
|
4209
|
+
} else {
|
|
4210
|
+
whereParts.push(`${qualify(w.field)} ${w.operator} ?`);
|
|
4211
|
+
params.push(transformValueForStorage(w.value));
|
|
4212
|
+
}
|
|
4213
|
+
}
|
|
4214
|
+
if (whereParts.length > 0) {
|
|
4215
|
+
sql += ` WHERE ${whereParts.join(" AND ")}`;
|
|
4216
|
+
}
|
|
4217
|
+
}
|
|
4218
|
+
if (iqo.whereOrs.length > 0) {
|
|
4219
|
+
for (const orGroup of iqo.whereOrs) {
|
|
4220
|
+
const orParts = [];
|
|
4221
|
+
for (const w of orGroup) {
|
|
4222
|
+
if (w.operator === "IN") {
|
|
4223
|
+
const arr = w.value;
|
|
4224
|
+
if (arr.length === 0) {
|
|
4225
|
+
orParts.push("1 = 0");
|
|
4226
|
+
} else {
|
|
4227
|
+
orParts.push(`${w.field} IN (${arr.map(() => "?").join(", ")})`);
|
|
4228
|
+
params.push(...arr.map(transformValueForStorage));
|
|
4229
|
+
}
|
|
4230
|
+
} else {
|
|
4231
|
+
orParts.push(`${w.field} ${w.operator} ?`);
|
|
4232
|
+
params.push(transformValueForStorage(w.value));
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4235
|
+
if (orParts.length > 0) {
|
|
4236
|
+
const orClause = `(${orParts.join(" OR ")})`;
|
|
4237
|
+
sql += sql.includes(" WHERE ") ? ` AND ${orClause}` : ` WHERE ${orClause}`;
|
|
4352
4238
|
}
|
|
4353
|
-
});
|
|
4354
|
-
if (isAsync(result)) {
|
|
4355
|
-
return result.then((result2) => {
|
|
4356
|
-
return {
|
|
4357
|
-
status: "valid",
|
|
4358
|
-
value: result2.status === "valid" ? result2.value : this._def.catchValue({
|
|
4359
|
-
get error() {
|
|
4360
|
-
return new ZodError(newCtx.common.issues);
|
|
4361
|
-
},
|
|
4362
|
-
input: newCtx.data
|
|
4363
|
-
})
|
|
4364
|
-
};
|
|
4365
|
-
});
|
|
4366
|
-
} else {
|
|
4367
|
-
return {
|
|
4368
|
-
status: "valid",
|
|
4369
|
-
value: result.status === "valid" ? result.value : this._def.catchValue({
|
|
4370
|
-
get error() {
|
|
4371
|
-
return new ZodError(newCtx.common.issues);
|
|
4372
|
-
},
|
|
4373
|
-
input: newCtx.data
|
|
4374
|
-
})
|
|
4375
|
-
};
|
|
4376
4239
|
}
|
|
4377
4240
|
}
|
|
4378
|
-
|
|
4379
|
-
|
|
4241
|
+
if (iqo.groupBy.length > 0) {
|
|
4242
|
+
sql += ` GROUP BY ${iqo.groupBy.join(", ")}`;
|
|
4380
4243
|
}
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
innerType: type,
|
|
4385
|
-
typeName: ZodFirstPartyTypeKind.ZodCatch,
|
|
4386
|
-
catchValue: typeof params.catch === "function" ? params.catch : () => params.catch,
|
|
4387
|
-
...processCreateParams(params)
|
|
4388
|
-
});
|
|
4389
|
-
};
|
|
4390
|
-
|
|
4391
|
-
class ZodNaN extends ZodType {
|
|
4392
|
-
_parse(input) {
|
|
4393
|
-
const parsedType = this._getType(input);
|
|
4394
|
-
if (parsedType !== ZodParsedType.nan) {
|
|
4395
|
-
const ctx = this._getOrReturnCtx(input);
|
|
4396
|
-
addIssueToContext(ctx, {
|
|
4397
|
-
code: ZodIssueCode.invalid_type,
|
|
4398
|
-
expected: ZodParsedType.nan,
|
|
4399
|
-
received: ctx.parsedType
|
|
4400
|
-
});
|
|
4401
|
-
return INVALID;
|
|
4402
|
-
}
|
|
4403
|
-
return { status: "valid", value: input.data };
|
|
4244
|
+
if (iqo.orderBy.length > 0) {
|
|
4245
|
+
const parts = iqo.orderBy.map((o) => `${o.field} ${o.direction.toUpperCase()}`);
|
|
4246
|
+
sql += ` ORDER BY ${parts.join(", ")}`;
|
|
4404
4247
|
}
|
|
4248
|
+
if (iqo.limit !== null)
|
|
4249
|
+
sql += ` LIMIT ${iqo.limit}`;
|
|
4250
|
+
if (iqo.offset !== null)
|
|
4251
|
+
sql += ` OFFSET ${iqo.offset}`;
|
|
4252
|
+
return { sql, params };
|
|
4405
4253
|
}
|
|
4406
|
-
ZodNaN.create = (params) => {
|
|
4407
|
-
return new ZodNaN({
|
|
4408
|
-
typeName: ZodFirstPartyTypeKind.ZodNaN,
|
|
4409
|
-
...processCreateParams(params)
|
|
4410
|
-
});
|
|
4411
|
-
};
|
|
4412
|
-
var BRAND = Symbol("zod_brand");
|
|
4413
4254
|
|
|
4414
|
-
class
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4255
|
+
class QueryBuilder {
|
|
4256
|
+
iqo;
|
|
4257
|
+
tableName;
|
|
4258
|
+
executor;
|
|
4259
|
+
singleExecutor;
|
|
4260
|
+
joinResolver;
|
|
4261
|
+
conditionResolver;
|
|
4262
|
+
eagerLoader;
|
|
4263
|
+
constructor(tableName, executor, singleExecutor, joinResolver, conditionResolver, eagerLoader) {
|
|
4264
|
+
this.tableName = tableName;
|
|
4265
|
+
this.executor = executor;
|
|
4266
|
+
this.singleExecutor = singleExecutor;
|
|
4267
|
+
this.joinResolver = joinResolver ?? null;
|
|
4268
|
+
this.conditionResolver = conditionResolver ?? null;
|
|
4269
|
+
this.eagerLoader = eagerLoader ?? null;
|
|
4270
|
+
this.iqo = {
|
|
4271
|
+
selects: [],
|
|
4272
|
+
wheres: [],
|
|
4273
|
+
whereOrs: [],
|
|
4274
|
+
whereAST: null,
|
|
4275
|
+
joins: [],
|
|
4276
|
+
groupBy: [],
|
|
4277
|
+
limit: null,
|
|
4278
|
+
offset: null,
|
|
4279
|
+
orderBy: [],
|
|
4280
|
+
includes: [],
|
|
4281
|
+
raw: false
|
|
4282
|
+
};
|
|
4423
4283
|
}
|
|
4424
|
-
|
|
4425
|
-
|
|
4284
|
+
select(...cols) {
|
|
4285
|
+
this.iqo.selects.push(...cols);
|
|
4286
|
+
return this;
|
|
4426
4287
|
}
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4288
|
+
where(criteriaOrCallback) {
|
|
4289
|
+
if (typeof criteriaOrCallback === "function") {
|
|
4290
|
+
const ast = criteriaOrCallback(createColumnProxy(), createFunctionProxy(), op);
|
|
4291
|
+
if (this.iqo.whereAST) {
|
|
4292
|
+
this.iqo.whereAST = { type: "operator", op: "AND", left: this.iqo.whereAST, right: ast };
|
|
4293
|
+
} else {
|
|
4294
|
+
this.iqo.whereAST = ast;
|
|
4295
|
+
}
|
|
4296
|
+
} else {
|
|
4297
|
+
const resolved = this.conditionResolver ? this.conditionResolver(criteriaOrCallback) : criteriaOrCallback;
|
|
4298
|
+
for (const [key, value] of Object.entries(resolved)) {
|
|
4299
|
+
if (key === "$or" && Array.isArray(value)) {
|
|
4300
|
+
const orConditions = [];
|
|
4301
|
+
for (const branch of value) {
|
|
4302
|
+
const resolvedBranch = this.conditionResolver ? this.conditionResolver(branch) : branch;
|
|
4303
|
+
for (const [bKey, bValue] of Object.entries(resolvedBranch)) {
|
|
4304
|
+
if (typeof bValue === "object" && bValue !== null && !Array.isArray(bValue) && !(bValue instanceof Date)) {
|
|
4305
|
+
for (const [opKey, operand] of Object.entries(bValue)) {
|
|
4306
|
+
const sqlOp = OPERATOR_MAP[opKey];
|
|
4307
|
+
if (sqlOp)
|
|
4308
|
+
orConditions.push({ field: bKey, operator: sqlOp, value: operand });
|
|
4309
|
+
}
|
|
4310
|
+
} else {
|
|
4311
|
+
orConditions.push({ field: bKey, operator: "=", value: bValue });
|
|
4312
|
+
}
|
|
4313
|
+
}
|
|
4314
|
+
}
|
|
4315
|
+
if (orConditions.length > 0)
|
|
4316
|
+
this.iqo.whereOrs.push(orConditions);
|
|
4317
|
+
continue;
|
|
4318
|
+
}
|
|
4319
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date)) {
|
|
4320
|
+
for (const [opKey, operand] of Object.entries(value)) {
|
|
4321
|
+
const sqlOp = OPERATOR_MAP[opKey];
|
|
4322
|
+
if (!sqlOp)
|
|
4323
|
+
throw new Error(`Unsupported query operator: '${opKey}' on field '${key}'.`);
|
|
4324
|
+
if (opKey === "$between") {
|
|
4325
|
+
if (!Array.isArray(operand) || operand.length !== 2)
|
|
4326
|
+
throw new Error(`$between for '${key}' requires [min, max]`);
|
|
4327
|
+
}
|
|
4328
|
+
this.iqo.wheres.push({
|
|
4329
|
+
field: key,
|
|
4330
|
+
operator: sqlOp,
|
|
4331
|
+
value: operand
|
|
4332
|
+
});
|
|
4333
|
+
}
|
|
4444
4334
|
} else {
|
|
4445
|
-
|
|
4446
|
-
data: inResult.value,
|
|
4447
|
-
path: ctx.path,
|
|
4448
|
-
parent: ctx
|
|
4449
|
-
});
|
|
4335
|
+
this.iqo.wheres.push({ field: key, operator: "=", value });
|
|
4450
4336
|
}
|
|
4451
|
-
}
|
|
4452
|
-
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4339
|
+
return this;
|
|
4340
|
+
}
|
|
4341
|
+
limit(n) {
|
|
4342
|
+
this.iqo.limit = n;
|
|
4343
|
+
return this;
|
|
4344
|
+
}
|
|
4345
|
+
offset(n) {
|
|
4346
|
+
this.iqo.offset = n;
|
|
4347
|
+
return this;
|
|
4348
|
+
}
|
|
4349
|
+
orderBy(field, direction = "asc") {
|
|
4350
|
+
this.iqo.orderBy.push({ field, direction });
|
|
4351
|
+
return this;
|
|
4352
|
+
}
|
|
4353
|
+
join(tableOrAccessor, fkOrCols, colsOrPk, pk) {
|
|
4354
|
+
let table;
|
|
4355
|
+
let fromCol;
|
|
4356
|
+
let toCol;
|
|
4357
|
+
let columns;
|
|
4358
|
+
if (typeof tableOrAccessor === "object" && "_tableName" in tableOrAccessor) {
|
|
4359
|
+
table = tableOrAccessor._tableName;
|
|
4360
|
+
columns = Array.isArray(fkOrCols) ? fkOrCols : [];
|
|
4361
|
+
if (!this.joinResolver)
|
|
4362
|
+
throw new Error(`Cannot auto-resolve join: no relationship data available`);
|
|
4363
|
+
const resolved = this.joinResolver(this.tableName, table);
|
|
4364
|
+
if (!resolved)
|
|
4365
|
+
throw new Error(`No relationship found between '${this.tableName}' and '${table}'`);
|
|
4366
|
+
fromCol = resolved.fk;
|
|
4367
|
+
toCol = resolved.pk;
|
|
4453
4368
|
} else {
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4369
|
+
table = tableOrAccessor;
|
|
4370
|
+
fromCol = fkOrCols;
|
|
4371
|
+
columns = Array.isArray(colsOrPk) ? colsOrPk : [];
|
|
4372
|
+
toCol = (typeof colsOrPk === "string" ? colsOrPk : pk) ?? "id";
|
|
4373
|
+
}
|
|
4374
|
+
this.iqo.joins.push({ table, fromCol, toCol, columns });
|
|
4375
|
+
this.iqo.raw = true;
|
|
4376
|
+
return this;
|
|
4377
|
+
}
|
|
4378
|
+
raw() {
|
|
4379
|
+
this.iqo.raw = true;
|
|
4380
|
+
return this;
|
|
4381
|
+
}
|
|
4382
|
+
with(...relations) {
|
|
4383
|
+
this.iqo.includes.push(...relations);
|
|
4384
|
+
return this;
|
|
4385
|
+
}
|
|
4386
|
+
_applyEagerLoads(results) {
|
|
4387
|
+
if (this.iqo.includes.length === 0 || !this.eagerLoader || results.length === 0) {
|
|
4388
|
+
return results;
|
|
4389
|
+
}
|
|
4390
|
+
const parentIds = results.map((r) => r.id).filter((id) => typeof id === "number");
|
|
4391
|
+
if (parentIds.length === 0)
|
|
4392
|
+
return results;
|
|
4393
|
+
for (const relation of this.iqo.includes) {
|
|
4394
|
+
const loaded = this.eagerLoader(this.tableName, relation, parentIds);
|
|
4395
|
+
if (!loaded)
|
|
4396
|
+
continue;
|
|
4397
|
+
for (const row of results) {
|
|
4398
|
+
row[loaded.key] = loaded.groups.get(row.id) ?? [];
|
|
4473
4399
|
}
|
|
4474
4400
|
}
|
|
4401
|
+
return results;
|
|
4402
|
+
}
|
|
4403
|
+
all() {
|
|
4404
|
+
const { sql, params } = compileIQO(this.tableName, this.iqo);
|
|
4405
|
+
const results = this.executor(sql, params, this.iqo.raw);
|
|
4406
|
+
return this._applyEagerLoads(results);
|
|
4407
|
+
}
|
|
4408
|
+
get() {
|
|
4409
|
+
this.iqo.limit = 1;
|
|
4410
|
+
const { sql, params } = compileIQO(this.tableName, this.iqo);
|
|
4411
|
+
const result = this.singleExecutor(sql, params, this.iqo.raw);
|
|
4412
|
+
if (!result)
|
|
4413
|
+
return null;
|
|
4414
|
+
const [loaded] = this._applyEagerLoads([result]);
|
|
4415
|
+
return loaded ?? null;
|
|
4475
4416
|
}
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4417
|
+
count() {
|
|
4418
|
+
const { sql: selectSql, params } = compileIQO(this.tableName, this.iqo);
|
|
4419
|
+
const countSql = selectSql.replace(/^SELECT .+? FROM/, "SELECT COUNT(*) as count FROM");
|
|
4420
|
+
const results = this.executor(countSql, params, true);
|
|
4421
|
+
return results[0]?.count ?? 0;
|
|
4422
|
+
}
|
|
4423
|
+
first() {
|
|
4424
|
+
return this.get();
|
|
4425
|
+
}
|
|
4426
|
+
exists() {
|
|
4427
|
+
const { sql: selectSql, params } = compileIQO(this.tableName, this.iqo);
|
|
4428
|
+
const existsSql = selectSql.replace(/^SELECT .+? FROM/, "SELECT 1 FROM").replace(/ LIMIT \d+/, "") + " LIMIT 1";
|
|
4429
|
+
const results = this.executor(existsSql, params, true);
|
|
4430
|
+
return results.length > 0;
|
|
4431
|
+
}
|
|
4432
|
+
groupBy(...fields) {
|
|
4433
|
+
this.iqo.groupBy.push(...fields);
|
|
4434
|
+
return this;
|
|
4435
|
+
}
|
|
4436
|
+
then(onfulfilled, onrejected) {
|
|
4437
|
+
try {
|
|
4438
|
+
const result = this.all();
|
|
4439
|
+
return Promise.resolve(result).then(onfulfilled, onrejected);
|
|
4440
|
+
} catch (err) {
|
|
4441
|
+
return Promise.reject(err).then(onfulfilled, onrejected);
|
|
4442
|
+
}
|
|
4482
4443
|
}
|
|
4483
4444
|
}
|
|
4484
4445
|
|
|
4485
|
-
class
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
return isAsync(result) ? result.then((data) => freeze(data)) : freeze(result);
|
|
4446
|
+
class ColumnNode {
|
|
4447
|
+
table;
|
|
4448
|
+
column;
|
|
4449
|
+
alias;
|
|
4450
|
+
_type = "COL";
|
|
4451
|
+
constructor(table, column, alias) {
|
|
4452
|
+
this.table = table;
|
|
4453
|
+
this.column = column;
|
|
4454
|
+
this.alias = alias;
|
|
4495
4455
|
}
|
|
4496
|
-
|
|
4497
|
-
return this.
|
|
4456
|
+
toString() {
|
|
4457
|
+
return `"${this.alias}"."${this.column}"`;
|
|
4458
|
+
}
|
|
4459
|
+
[Symbol.toPrimitive]() {
|
|
4460
|
+
return this.toString();
|
|
4498
4461
|
}
|
|
4499
4462
|
}
|
|
4500
|
-
|
|
4501
|
-
return
|
|
4502
|
-
innerType: type,
|
|
4503
|
-
typeName: ZodFirstPartyTypeKind.ZodReadonly,
|
|
4504
|
-
...processCreateParams(params)
|
|
4505
|
-
});
|
|
4506
|
-
};
|
|
4507
|
-
function cleanParams(params, data) {
|
|
4508
|
-
const p = typeof params === "function" ? params(data) : typeof params === "string" ? { message: params } : params;
|
|
4509
|
-
const p2 = typeof p === "string" ? { message: p } : p;
|
|
4510
|
-
return p2;
|
|
4463
|
+
function q(name) {
|
|
4464
|
+
return `"${name}"`;
|
|
4511
4465
|
}
|
|
4512
|
-
function
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
const _fatal = params.fatal ?? fatal ?? true;
|
|
4521
|
-
ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
|
|
4522
|
-
}
|
|
4523
|
-
});
|
|
4466
|
+
function qRef(alias, column) {
|
|
4467
|
+
return `"${alias}"."${column}"`;
|
|
4468
|
+
}
|
|
4469
|
+
function createTableProxy(tableName, alias, columns) {
|
|
4470
|
+
return new Proxy({}, {
|
|
4471
|
+
get(_target, prop) {
|
|
4472
|
+
if (prop === Symbol.toPrimitive || prop === "toString" || prop === "valueOf") {
|
|
4473
|
+
return;
|
|
4524
4474
|
}
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4475
|
+
return new ColumnNode(tableName, prop, alias);
|
|
4476
|
+
},
|
|
4477
|
+
ownKeys() {
|
|
4478
|
+
return [...columns];
|
|
4479
|
+
},
|
|
4480
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
4481
|
+
if (columns.has(prop)) {
|
|
4482
|
+
return { configurable: true, enumerable: true, value: new ColumnNode(tableName, prop, alias) };
|
|
4529
4483
|
}
|
|
4530
4484
|
return;
|
|
4531
|
-
}
|
|
4532
|
-
|
|
4485
|
+
}
|
|
4486
|
+
});
|
|
4533
4487
|
}
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
(
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
}
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
var objectType = ZodObject.create;
|
|
4594
|
-
var strictObjectType = ZodObject.strictCreate;
|
|
4595
|
-
var unionType = ZodUnion.create;
|
|
4596
|
-
var discriminatedUnionType = ZodDiscriminatedUnion.create;
|
|
4597
|
-
var intersectionType = ZodIntersection.create;
|
|
4598
|
-
var tupleType = ZodTuple.create;
|
|
4599
|
-
var recordType = ZodRecord.create;
|
|
4600
|
-
var mapType = ZodMap.create;
|
|
4601
|
-
var setType = ZodSet.create;
|
|
4602
|
-
var functionType = ZodFunction.create;
|
|
4603
|
-
var lazyType = ZodLazy.create;
|
|
4604
|
-
var literalType = ZodLiteral.create;
|
|
4605
|
-
var enumType = ZodEnum.create;
|
|
4606
|
-
var nativeEnumType = ZodNativeEnum.create;
|
|
4607
|
-
var promiseType = ZodPromise.create;
|
|
4608
|
-
var effectsType = ZodEffects.create;
|
|
4609
|
-
var optionalType = ZodOptional.create;
|
|
4610
|
-
var nullableType = ZodNullable.create;
|
|
4611
|
-
var preprocessType = ZodEffects.createWithPreprocess;
|
|
4612
|
-
var pipelineType = ZodPipeline.create;
|
|
4613
|
-
var ostring = () => stringType().optional();
|
|
4614
|
-
var onumber = () => numberType().optional();
|
|
4615
|
-
var oboolean = () => booleanType().optional();
|
|
4616
|
-
var coerce = {
|
|
4617
|
-
string: (arg) => ZodString.create({ ...arg, coerce: true }),
|
|
4618
|
-
number: (arg) => ZodNumber.create({ ...arg, coerce: true }),
|
|
4619
|
-
boolean: (arg) => ZodBoolean.create({
|
|
4620
|
-
...arg,
|
|
4621
|
-
coerce: true
|
|
4622
|
-
}),
|
|
4623
|
-
bigint: (arg) => ZodBigInt.create({ ...arg, coerce: true }),
|
|
4624
|
-
date: (arg) => ZodDate.create({ ...arg, coerce: true })
|
|
4625
|
-
};
|
|
4626
|
-
var NEVER = INVALID;
|
|
4627
|
-
// src/schema.ts
|
|
4628
|
-
function parseRelationsConfig(relations, schemas) {
|
|
4629
|
-
const relationships = [];
|
|
4630
|
-
const added = new Set;
|
|
4631
|
-
for (const [fromTable, rels] of Object.entries(relations)) {
|
|
4632
|
-
if (!schemas[fromTable]) {
|
|
4633
|
-
throw new Error(`relations: unknown table '${fromTable}'`);
|
|
4488
|
+
function createContextProxy(schemas) {
|
|
4489
|
+
const aliases = new Map;
|
|
4490
|
+
let aliasCounter = 0;
|
|
4491
|
+
const proxy = new Proxy({}, {
|
|
4492
|
+
get(_target, tableName) {
|
|
4493
|
+
if (typeof tableName !== "string")
|
|
4494
|
+
return;
|
|
4495
|
+
const schema = schemas[tableName];
|
|
4496
|
+
const shape = schema ? schema.shape : {};
|
|
4497
|
+
const columns = new Set(Object.keys(shape));
|
|
4498
|
+
aliasCounter++;
|
|
4499
|
+
const alias = `t${aliasCounter}`;
|
|
4500
|
+
const tableProxy = createTableProxy(tableName, alias, columns);
|
|
4501
|
+
const entries = aliases.get(tableName) || [];
|
|
4502
|
+
entries.push({ tableName, alias, proxy: tableProxy });
|
|
4503
|
+
aliases.set(tableName, entries);
|
|
4504
|
+
return tableProxy;
|
|
4505
|
+
}
|
|
4506
|
+
});
|
|
4507
|
+
return { proxy, aliasMap: aliases };
|
|
4508
|
+
}
|
|
4509
|
+
function isColumnNode(val) {
|
|
4510
|
+
return val && typeof val === "object" && val._type === "COL";
|
|
4511
|
+
}
|
|
4512
|
+
function compileProxyQuery(queryResult, aliasMap) {
|
|
4513
|
+
const params = [];
|
|
4514
|
+
const tablesUsed = new Map;
|
|
4515
|
+
for (const [tableName, entries] of aliasMap) {
|
|
4516
|
+
for (const entry of entries) {
|
|
4517
|
+
tablesUsed.set(entry.alias, { tableName, alias: entry.alias });
|
|
4518
|
+
}
|
|
4519
|
+
}
|
|
4520
|
+
const selectParts = [];
|
|
4521
|
+
for (const [outputName, colOrValue] of Object.entries(queryResult.select)) {
|
|
4522
|
+
if (isColumnNode(colOrValue)) {
|
|
4523
|
+
if (outputName === colOrValue.column) {
|
|
4524
|
+
selectParts.push(qRef(colOrValue.alias, colOrValue.column));
|
|
4525
|
+
} else {
|
|
4526
|
+
selectParts.push(`${qRef(colOrValue.alias, colOrValue.column)} AS ${q(outputName)}`);
|
|
4527
|
+
}
|
|
4528
|
+
} else {
|
|
4529
|
+
selectParts.push(`? AS ${q(outputName)}`);
|
|
4530
|
+
params.push(colOrValue);
|
|
4531
|
+
}
|
|
4532
|
+
}
|
|
4533
|
+
const allAliases = [...tablesUsed.values()];
|
|
4534
|
+
if (allAliases.length === 0)
|
|
4535
|
+
throw new Error("No tables referenced in query.");
|
|
4536
|
+
const primaryAlias = allAliases[0];
|
|
4537
|
+
let sql = `SELECT ${selectParts.join(", ")} FROM ${q(primaryAlias.tableName)} ${q(primaryAlias.alias)}`;
|
|
4538
|
+
if (queryResult.join) {
|
|
4539
|
+
const joins = Array.isArray(queryResult.join[0]) ? queryResult.join : [queryResult.join];
|
|
4540
|
+
for (const [left, right] of joins) {
|
|
4541
|
+
const leftTable = tablesUsed.get(left.alias);
|
|
4542
|
+
const rightTable = tablesUsed.get(right.alias);
|
|
4543
|
+
if (!leftTable || !rightTable)
|
|
4544
|
+
throw new Error("Join references unknown table alias.");
|
|
4545
|
+
const joinAlias = leftTable.alias === primaryAlias.alias ? rightTable : leftTable;
|
|
4546
|
+
sql += ` JOIN ${q(joinAlias.tableName)} ${q(joinAlias.alias)} ON ${qRef(left.alias, left.column)} = ${qRef(right.alias, right.column)}`;
|
|
4634
4547
|
}
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4548
|
+
}
|
|
4549
|
+
if (queryResult.where && Object.keys(queryResult.where).length > 0) {
|
|
4550
|
+
const whereParts = [];
|
|
4551
|
+
for (const [key, value] of Object.entries(queryResult.where)) {
|
|
4552
|
+
let fieldRef;
|
|
4553
|
+
const quotedMatch = key.match(/^"([^"]+)"\.\"([^"]+)"$/);
|
|
4554
|
+
if (quotedMatch && tablesUsed.has(quotedMatch[1])) {
|
|
4555
|
+
fieldRef = key;
|
|
4556
|
+
} else {
|
|
4557
|
+
fieldRef = qRef(primaryAlias.alias, key);
|
|
4638
4558
|
}
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
if (
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
}
|
|
4649
|
-
|
|
4559
|
+
if (isColumnNode(value)) {
|
|
4560
|
+
whereParts.push(`${fieldRef} = ${qRef(value.alias, value.column)}`);
|
|
4561
|
+
} else if (Array.isArray(value)) {
|
|
4562
|
+
if (value.length === 0) {
|
|
4563
|
+
whereParts.push("1 = 0");
|
|
4564
|
+
} else {
|
|
4565
|
+
const placeholders = value.map(() => "?").join(", ");
|
|
4566
|
+
whereParts.push(`${fieldRef} IN (${placeholders})`);
|
|
4567
|
+
params.push(...value);
|
|
4568
|
+
}
|
|
4569
|
+
} else if (typeof value === "object" && value !== null && !(value instanceof Date)) {
|
|
4570
|
+
for (const [pOp, operand] of Object.entries(value)) {
|
|
4571
|
+
if (pOp === "$in") {
|
|
4572
|
+
const arr = operand;
|
|
4573
|
+
if (arr.length === 0) {
|
|
4574
|
+
whereParts.push("1 = 0");
|
|
4575
|
+
} else {
|
|
4576
|
+
const placeholders = arr.map(() => "?").join(", ");
|
|
4577
|
+
whereParts.push(`${fieldRef} IN (${placeholders})`);
|
|
4578
|
+
params.push(...arr);
|
|
4579
|
+
}
|
|
4580
|
+
continue;
|
|
4581
|
+
}
|
|
4582
|
+
const opMap = {
|
|
4583
|
+
$gt: ">",
|
|
4584
|
+
$gte: ">=",
|
|
4585
|
+
$lt: "<",
|
|
4586
|
+
$lte: "<=",
|
|
4587
|
+
$ne: "!="
|
|
4588
|
+
};
|
|
4589
|
+
const sqlOp = opMap[pOp];
|
|
4590
|
+
if (!sqlOp)
|
|
4591
|
+
throw new Error(`Unsupported where operator: ${pOp}`);
|
|
4592
|
+
whereParts.push(`${fieldRef} ${sqlOp} ?`);
|
|
4593
|
+
params.push(operand);
|
|
4594
|
+
}
|
|
4595
|
+
} else {
|
|
4596
|
+
whereParts.push(`${fieldRef} = ?`);
|
|
4597
|
+
params.push(value instanceof Date ? value.toISOString() : value);
|
|
4650
4598
|
}
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4599
|
+
}
|
|
4600
|
+
if (whereParts.length > 0) {
|
|
4601
|
+
sql += ` WHERE ${whereParts.join(" AND ")}`;
|
|
4602
|
+
}
|
|
4603
|
+
}
|
|
4604
|
+
if (queryResult.orderBy) {
|
|
4605
|
+
const parts = [];
|
|
4606
|
+
for (const [key, dir] of Object.entries(queryResult.orderBy)) {
|
|
4607
|
+
let fieldRef;
|
|
4608
|
+
const quotedMatch = key.match(/^"([^"]+)"\.\"([^"]+)"$/);
|
|
4609
|
+
if (quotedMatch && tablesUsed.has(quotedMatch[1])) {
|
|
4610
|
+
fieldRef = key;
|
|
4611
|
+
} else {
|
|
4612
|
+
fieldRef = qRef(primaryAlias.alias, key);
|
|
4661
4613
|
}
|
|
4614
|
+
parts.push(`${fieldRef} ${dir.toUpperCase()}`);
|
|
4615
|
+
}
|
|
4616
|
+
if (parts.length > 0) {
|
|
4617
|
+
sql += ` ORDER BY ${parts.join(", ")}`;
|
|
4662
4618
|
}
|
|
4663
4619
|
}
|
|
4664
|
-
|
|
4620
|
+
if (queryResult.groupBy && queryResult.groupBy.length > 0) {
|
|
4621
|
+
const parts = queryResult.groupBy.filter(Boolean).map((col) => qRef(col.alias, col.column));
|
|
4622
|
+
sql += ` GROUP BY ${parts.join(", ")}`;
|
|
4623
|
+
}
|
|
4624
|
+
if (queryResult.limit !== undefined)
|
|
4625
|
+
sql += ` LIMIT ${queryResult.limit}`;
|
|
4626
|
+
if (queryResult.offset !== undefined)
|
|
4627
|
+
sql += ` OFFSET ${queryResult.offset}`;
|
|
4628
|
+
return { sql, params };
|
|
4665
4629
|
}
|
|
4666
|
-
function
|
|
4667
|
-
|
|
4630
|
+
function executeProxyQuery(schemas, callback, executor) {
|
|
4631
|
+
const { proxy, aliasMap } = createContextProxy(schemas);
|
|
4632
|
+
const queryResult = callback(proxy);
|
|
4633
|
+
const { sql, params } = compileProxyQuery(queryResult, aliasMap);
|
|
4634
|
+
return executor(sql, params);
|
|
4668
4635
|
}
|
|
4669
|
-
function
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
return
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4636
|
+
function createQueryBuilder(ctx, entityName, initialCols) {
|
|
4637
|
+
const schema = ctx.schemas[entityName];
|
|
4638
|
+
const executor = (sql, params, raw) => {
|
|
4639
|
+
const rows = ctx.db.query(sql).all(...params);
|
|
4640
|
+
if (raw)
|
|
4641
|
+
return rows;
|
|
4642
|
+
return rows.map((row) => ctx.attachMethods(entityName, transformFromStorage(row, schema)));
|
|
4643
|
+
};
|
|
4644
|
+
const singleExecutor = (sql, params, raw) => {
|
|
4645
|
+
const results = executor(sql, params, raw);
|
|
4646
|
+
return results.length > 0 ? results[0] : null;
|
|
4647
|
+
};
|
|
4648
|
+
const joinResolver = (fromTable, toTable) => {
|
|
4649
|
+
const belongsTo = ctx.relationships.find((r) => r.type === "belongs-to" && r.from === fromTable && r.to === toTable);
|
|
4650
|
+
if (belongsTo)
|
|
4651
|
+
return { fk: belongsTo.foreignKey, pk: "id" };
|
|
4652
|
+
const reverse = ctx.relationships.find((r) => r.type === "belongs-to" && r.from === toTable && r.to === fromTable);
|
|
4653
|
+
if (reverse)
|
|
4654
|
+
return { fk: "id", pk: reverse.foreignKey };
|
|
4655
|
+
return null;
|
|
4656
|
+
};
|
|
4657
|
+
const conditionResolver = (conditions) => {
|
|
4658
|
+
const resolved = {};
|
|
4659
|
+
for (const [key, value] of Object.entries(conditions)) {
|
|
4660
|
+
if (value && typeof value === "object" && typeof value.id === "number" && typeof value.delete === "function") {
|
|
4661
|
+
const fkCol = key + "_id";
|
|
4662
|
+
const rel = ctx.relationships.find((r) => r.type === "belongs-to" && r.from === entityName && r.foreignKey === fkCol);
|
|
4663
|
+
if (rel) {
|
|
4664
|
+
resolved[fkCol] = value.id;
|
|
4665
|
+
} else {
|
|
4666
|
+
const relByNav = ctx.relationships.find((r) => r.type === "belongs-to" && r.from === entityName && r.to === key + "s") || ctx.relationships.find((r) => r.type === "belongs-to" && r.from === entityName && r.to === key);
|
|
4667
|
+
if (relByNav) {
|
|
4668
|
+
resolved[relByNav.foreignKey] = value.id;
|
|
4669
|
+
} else {
|
|
4670
|
+
resolved[key] = value;
|
|
4671
|
+
}
|
|
4672
|
+
}
|
|
4673
|
+
} else {
|
|
4674
|
+
resolved[key] = value;
|
|
4675
|
+
}
|
|
4676
|
+
}
|
|
4677
|
+
return resolved;
|
|
4678
|
+
};
|
|
4679
|
+
const eagerLoader = (parentTable, relation, parentIds) => {
|
|
4680
|
+
const hasMany = ctx.relationships.find((r) => r.type === "one-to-many" && r.from === parentTable && r.relationshipField === relation);
|
|
4681
|
+
if (hasMany) {
|
|
4682
|
+
const belongsTo2 = ctx.relationships.find((r) => r.type === "belongs-to" && r.from === hasMany.to && r.to === parentTable);
|
|
4683
|
+
if (belongsTo2) {
|
|
4684
|
+
const fk = belongsTo2.foreignKey;
|
|
4685
|
+
const placeholders = parentIds.map(() => "?").join(", ");
|
|
4686
|
+
const childRows = ctx.db.query(`SELECT * FROM ${hasMany.to} WHERE ${fk} IN (${placeholders})`).all(...parentIds);
|
|
4687
|
+
const groups = new Map;
|
|
4688
|
+
const childSchema = ctx.schemas[hasMany.to];
|
|
4689
|
+
for (const rawRow of childRows) {
|
|
4690
|
+
const entity = ctx.attachMethods(hasMany.to, transformFromStorage(rawRow, childSchema));
|
|
4691
|
+
const parentId = rawRow[fk];
|
|
4692
|
+
if (!groups.has(parentId))
|
|
4693
|
+
groups.set(parentId, []);
|
|
4694
|
+
groups.get(parentId).push(entity);
|
|
4695
|
+
}
|
|
4696
|
+
return { key: relation, groups };
|
|
4697
|
+
}
|
|
4698
|
+
}
|
|
4699
|
+
const belongsTo = ctx.relationships.find((r) => r.type === "belongs-to" && r.from === parentTable && r.relationshipField === relation);
|
|
4700
|
+
if (belongsTo) {
|
|
4701
|
+
return null;
|
|
4702
|
+
}
|
|
4703
|
+
return null;
|
|
4704
|
+
};
|
|
4705
|
+
const builder = new QueryBuilder(entityName, executor, singleExecutor, joinResolver, conditionResolver, eagerLoader);
|
|
4706
|
+
if (initialCols.length > 0)
|
|
4707
|
+
builder.select(...initialCols);
|
|
4708
|
+
return builder;
|
|
4683
4709
|
}
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4710
|
+
|
|
4711
|
+
// src/helpers.ts
|
|
4712
|
+
function buildWhereClause(conditions, tablePrefix) {
|
|
4713
|
+
const parts = [];
|
|
4714
|
+
const values = [];
|
|
4715
|
+
for (const key in conditions) {
|
|
4716
|
+
if (key.startsWith("$")) {
|
|
4717
|
+
if (key === "$or" && Array.isArray(conditions[key])) {
|
|
4718
|
+
const orBranches = conditions[key];
|
|
4719
|
+
const orParts = [];
|
|
4720
|
+
for (const branch of orBranches) {
|
|
4721
|
+
const sub = buildWhereClause(branch, tablePrefix);
|
|
4722
|
+
if (sub.clause) {
|
|
4723
|
+
orParts.push(`(${sub.clause.replace(/^WHERE /, "")})`);
|
|
4724
|
+
values.push(...sub.values);
|
|
4725
|
+
}
|
|
4726
|
+
}
|
|
4727
|
+
if (orParts.length > 0)
|
|
4728
|
+
parts.push(`(${orParts.join(" OR ")})`);
|
|
4729
|
+
}
|
|
4730
|
+
continue;
|
|
4731
|
+
}
|
|
4732
|
+
const value = conditions[key];
|
|
4733
|
+
const fieldName = tablePrefix ? `"${tablePrefix}"."${key}"` : `"${key}"`;
|
|
4734
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date)) {
|
|
4735
|
+
const operator = Object.keys(value)[0];
|
|
4736
|
+
if (!operator?.startsWith("$")) {
|
|
4737
|
+
throw new Error(`Querying on nested object '${key}' not supported. Use operators like $gt.`);
|
|
4738
|
+
}
|
|
4739
|
+
const operand = value[operator];
|
|
4740
|
+
if (operator === "$in") {
|
|
4741
|
+
if (!Array.isArray(operand))
|
|
4742
|
+
throw new Error(`$in for '${key}' requires an array`);
|
|
4743
|
+
if (operand.length === 0) {
|
|
4744
|
+
parts.push("1 = 0");
|
|
4745
|
+
continue;
|
|
4746
|
+
}
|
|
4747
|
+
parts.push(`${fieldName} IN (${operand.map(() => "?").join(", ")})`);
|
|
4748
|
+
values.push(...operand.map((v) => transformForStorage({ v }).v));
|
|
4749
|
+
continue;
|
|
4750
|
+
}
|
|
4751
|
+
if (operator === "$notIn") {
|
|
4752
|
+
if (!Array.isArray(operand))
|
|
4753
|
+
throw new Error(`$notIn for '${key}' requires an array`);
|
|
4754
|
+
if (operand.length === 0)
|
|
4755
|
+
continue;
|
|
4756
|
+
parts.push(`${fieldName} NOT IN (${operand.map(() => "?").join(", ")})`);
|
|
4757
|
+
values.push(...operand.map((v) => transformForStorage({ v }).v));
|
|
4758
|
+
continue;
|
|
4759
|
+
}
|
|
4760
|
+
if (operator === "$like") {
|
|
4761
|
+
parts.push(`${fieldName} LIKE ?`);
|
|
4762
|
+
values.push(operand);
|
|
4763
|
+
continue;
|
|
4764
|
+
}
|
|
4765
|
+
if (operator === "$between") {
|
|
4766
|
+
if (!Array.isArray(operand) || operand.length !== 2)
|
|
4767
|
+
throw new Error(`$between for '${key}' requires [min, max]`);
|
|
4768
|
+
parts.push(`${fieldName} BETWEEN ? AND ?`);
|
|
4769
|
+
values.push(transformForStorage({ v: operand[0] }).v, transformForStorage({ v: operand[1] }).v);
|
|
4770
|
+
continue;
|
|
4771
|
+
}
|
|
4772
|
+
const sqlOp = { $gt: ">", $gte: ">=", $lt: "<", $lte: "<=", $ne: "!=" }[operator];
|
|
4773
|
+
if (!sqlOp)
|
|
4774
|
+
throw new Error(`Unsupported operator '${operator}' on '${key}'`);
|
|
4775
|
+
parts.push(`${fieldName} ${sqlOp} ?`);
|
|
4776
|
+
values.push(transformForStorage({ operand }).operand);
|
|
4691
4777
|
} else {
|
|
4692
|
-
|
|
4778
|
+
parts.push(`${fieldName} = ?`);
|
|
4779
|
+
values.push(transformForStorage({ value }).value);
|
|
4693
4780
|
}
|
|
4694
4781
|
}
|
|
4695
|
-
return
|
|
4782
|
+
return { clause: parts.length > 0 ? `WHERE ${parts.join(" AND ")}` : "", values };
|
|
4696
4783
|
}
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4784
|
+
|
|
4785
|
+
// src/crud.ts
|
|
4786
|
+
function getById(ctx, entityName, id) {
|
|
4787
|
+
const row = ctx.db.query(`SELECT * FROM "${entityName}" WHERE id = ?`).get(id);
|
|
4788
|
+
if (!row)
|
|
4789
|
+
return null;
|
|
4790
|
+
return ctx.attachMethods(entityName, transformFromStorage(row, ctx.schemas[entityName]));
|
|
4791
|
+
}
|
|
4792
|
+
function getOne(ctx, entityName, conditions) {
|
|
4793
|
+
const { clause, values } = ctx.buildWhereClause(conditions);
|
|
4794
|
+
const row = ctx.db.query(`SELECT * FROM "${entityName}" ${clause} LIMIT 1`).get(...values);
|
|
4795
|
+
if (!row)
|
|
4796
|
+
return null;
|
|
4797
|
+
return ctx.attachMethods(entityName, transformFromStorage(row, ctx.schemas[entityName]));
|
|
4798
|
+
}
|
|
4799
|
+
function findMany(ctx, entityName, conditions = {}) {
|
|
4800
|
+
const { clause, values } = ctx.buildWhereClause(conditions);
|
|
4801
|
+
const rows = ctx.db.query(`SELECT * FROM "${entityName}" ${clause}`).all(...values);
|
|
4802
|
+
return rows.map((row) => ctx.attachMethods(entityName, transformFromStorage(row, ctx.schemas[entityName])));
|
|
4803
|
+
}
|
|
4804
|
+
function insert(ctx, entityName, data) {
|
|
4805
|
+
const schema = ctx.schemas[entityName];
|
|
4806
|
+
const validatedData = asZodObject(schema).passthrough().parse(data);
|
|
4807
|
+
const transformed = transformForStorage(validatedData);
|
|
4808
|
+
const columns = Object.keys(transformed);
|
|
4809
|
+
const quotedCols = columns.map((c) => `"${c}"`);
|
|
4810
|
+
const sql = columns.length === 0 ? `INSERT INTO "${entityName}" DEFAULT VALUES` : `INSERT INTO "${entityName}" (${quotedCols.join(", ")}) VALUES (${columns.map(() => "?").join(", ")})`;
|
|
4811
|
+
const result = ctx.db.query(sql).run(...Object.values(transformed));
|
|
4812
|
+
const newEntity = getById(ctx, entityName, result.lastInsertRowid);
|
|
4813
|
+
if (!newEntity)
|
|
4814
|
+
throw new Error("Failed to retrieve entity after insertion");
|
|
4815
|
+
return newEntity;
|
|
4816
|
+
}
|
|
4817
|
+
function update(ctx, entityName, id, data) {
|
|
4818
|
+
const schema = ctx.schemas[entityName];
|
|
4819
|
+
const validatedData = asZodObject(schema).partial().parse(data);
|
|
4820
|
+
const transformed = transformForStorage(validatedData);
|
|
4821
|
+
if (Object.keys(transformed).length === 0)
|
|
4822
|
+
return getById(ctx, entityName, id);
|
|
4823
|
+
const setClause = Object.keys(transformed).map((key) => `"${key}" = ?`).join(", ");
|
|
4824
|
+
ctx.db.query(`UPDATE "${entityName}" SET ${setClause} WHERE id = ?`).run(...Object.values(transformed), id);
|
|
4825
|
+
return getById(ctx, entityName, id);
|
|
4826
|
+
}
|
|
4827
|
+
function updateWhere(ctx, entityName, data, conditions) {
|
|
4828
|
+
const schema = ctx.schemas[entityName];
|
|
4829
|
+
const validatedData = asZodObject(schema).partial().parse(data);
|
|
4830
|
+
const transformed = transformForStorage(validatedData);
|
|
4831
|
+
if (Object.keys(transformed).length === 0)
|
|
4832
|
+
return 0;
|
|
4833
|
+
const { clause, values: whereValues } = ctx.buildWhereClause(conditions);
|
|
4834
|
+
if (!clause)
|
|
4835
|
+
throw new Error("update().where() requires at least one condition");
|
|
4836
|
+
const setCols = Object.keys(transformed);
|
|
4837
|
+
const setClause = setCols.map((key) => `"${key}" = ?`).join(", ");
|
|
4838
|
+
const result = ctx.db.query(`UPDATE "${entityName}" SET ${setClause} ${clause}`).run(...setCols.map((key) => transformed[key]), ...whereValues);
|
|
4839
|
+
return result.changes ?? 0;
|
|
4840
|
+
}
|
|
4841
|
+
function createUpdateBuilder(ctx, entityName, data) {
|
|
4842
|
+
let _conditions = {};
|
|
4843
|
+
const builder = {
|
|
4844
|
+
where: (conditions) => {
|
|
4845
|
+
_conditions = { ..._conditions, ...conditions };
|
|
4846
|
+
return builder;
|
|
4847
|
+
},
|
|
4848
|
+
exec: () => updateWhere(ctx, entityName, data, _conditions)
|
|
4849
|
+
};
|
|
4850
|
+
return builder;
|
|
4851
|
+
}
|
|
4852
|
+
function upsert(ctx, entityName, data, conditions = {}) {
|
|
4853
|
+
const hasId = data?.id && typeof data.id === "number";
|
|
4854
|
+
const existing = hasId ? getById(ctx, entityName, data.id) : Object.keys(conditions ?? {}).length > 0 ? getOne(ctx, entityName, conditions) : null;
|
|
4855
|
+
if (existing) {
|
|
4856
|
+
const updateData = { ...data };
|
|
4857
|
+
delete updateData.id;
|
|
4858
|
+
return update(ctx, entityName, existing.id, updateData);
|
|
4859
|
+
}
|
|
4860
|
+
const insertData = { ...conditions ?? {}, ...data ?? {} };
|
|
4861
|
+
delete insertData.id;
|
|
4862
|
+
return insert(ctx, entityName, insertData);
|
|
4863
|
+
}
|
|
4864
|
+
function deleteEntity(ctx, entityName, id) {
|
|
4865
|
+
ctx.db.query(`DELETE FROM "${entityName}" WHERE id = ?`).run(id);
|
|
4866
|
+
}
|
|
4867
|
+
function insertMany(ctx, entityName, rows) {
|
|
4868
|
+
if (rows.length === 0)
|
|
4869
|
+
return [];
|
|
4870
|
+
const schema = ctx.schemas[entityName];
|
|
4871
|
+
const zodSchema = asZodObject(schema).passthrough();
|
|
4872
|
+
const txn = ctx.db.transaction(() => {
|
|
4873
|
+
const ids2 = [];
|
|
4874
|
+
for (const data of rows) {
|
|
4875
|
+
const validatedData = zodSchema.parse(data);
|
|
4876
|
+
const transformed = transformForStorage(validatedData);
|
|
4877
|
+
const columns = Object.keys(transformed);
|
|
4878
|
+
const quotedCols = columns.map((c) => `"${c}"`);
|
|
4879
|
+
const sql = columns.length === 0 ? `INSERT INTO "${entityName}" DEFAULT VALUES` : `INSERT INTO "${entityName}" (${quotedCols.join(", ")}) VALUES (${columns.map(() => "?").join(", ")})`;
|
|
4880
|
+
const result = ctx.db.query(sql).run(...Object.values(transformed));
|
|
4881
|
+
ids2.push(result.lastInsertRowid);
|
|
4882
|
+
}
|
|
4883
|
+
return ids2;
|
|
4884
|
+
});
|
|
4885
|
+
const ids = txn();
|
|
4886
|
+
return ids.map((id) => getById(ctx, entityName, id)).filter(Boolean);
|
|
4887
|
+
}
|
|
4888
|
+
|
|
4889
|
+
// src/entity.ts
|
|
4890
|
+
function attachMethods(ctx, entityName, entity) {
|
|
4891
|
+
const augmented = entity;
|
|
4892
|
+
augmented.update = (data) => update(ctx, entityName, entity.id, data);
|
|
4893
|
+
augmented.delete = () => deleteEntity(ctx, entityName, entity.id);
|
|
4894
|
+
for (const rel of ctx.relationships) {
|
|
4895
|
+
if (rel.from === entityName && rel.type === "belongs-to") {
|
|
4896
|
+
augmented[rel.relationshipField] = () => {
|
|
4897
|
+
const fkValue = entity[rel.foreignKey];
|
|
4898
|
+
return fkValue ? getById(ctx, rel.to, fkValue) : null;
|
|
4899
|
+
};
|
|
4900
|
+
} else if (rel.from === entityName && rel.type === "one-to-many") {
|
|
4901
|
+
const belongsToRel = ctx.relationships.find((r) => r.type === "belongs-to" && r.from === rel.to && r.to === rel.from);
|
|
4902
|
+
if (belongsToRel) {
|
|
4903
|
+
const fk = belongsToRel.foreignKey;
|
|
4904
|
+
augmented[rel.relationshipField] = () => {
|
|
4905
|
+
return findMany(ctx, rel.to, { [fk]: entity.id });
|
|
4906
|
+
};
|
|
4907
|
+
}
|
|
4713
4908
|
}
|
|
4714
4909
|
}
|
|
4715
|
-
|
|
4910
|
+
const storableFieldNames = new Set(getStorableFields(ctx.schemas[entityName]).map((f) => f.name));
|
|
4911
|
+
return new Proxy(augmented, {
|
|
4912
|
+
set: (target, prop, value) => {
|
|
4913
|
+
if (storableFieldNames.has(prop) && target[prop] !== value) {
|
|
4914
|
+
update(ctx, entityName, target.id, { [prop]: value });
|
|
4915
|
+
}
|
|
4916
|
+
target[prop] = value;
|
|
4917
|
+
return true;
|
|
4918
|
+
},
|
|
4919
|
+
get: (target, prop, receiver) => Reflect.get(target, prop, receiver)
|
|
4920
|
+
});
|
|
4716
4921
|
}
|
|
4717
4922
|
|
|
4718
4923
|
// src/database.ts
|
|
4719
4924
|
class _Database {
|
|
4720
4925
|
db;
|
|
4926
|
+
_reactive;
|
|
4721
4927
|
schemas;
|
|
4722
4928
|
relationships;
|
|
4723
4929
|
options;
|
|
4724
|
-
|
|
4725
|
-
|
|
4930
|
+
_ctx;
|
|
4931
|
+
_listeners = [];
|
|
4932
|
+
_changeWatermark = 0;
|
|
4933
|
+
_pollTimer = null;
|
|
4934
|
+
_pollInterval;
|
|
4726
4935
|
constructor(dbFile, schemas, options = {}) {
|
|
4727
4936
|
this.db = new SqliteDatabase(dbFile);
|
|
4728
4937
|
this.db.run("PRAGMA journal_mode = WAL");
|
|
4729
4938
|
this.db.run("PRAGMA foreign_keys = ON");
|
|
4730
4939
|
this.schemas = schemas;
|
|
4731
4940
|
this.options = options;
|
|
4732
|
-
this.
|
|
4941
|
+
this._reactive = options.reactive !== false;
|
|
4942
|
+
this._pollInterval = options.pollInterval ?? 100;
|
|
4733
4943
|
this.relationships = options.relations ? parseRelationsConfig(options.relations, schemas) : [];
|
|
4944
|
+
this._ctx = {
|
|
4945
|
+
db: this.db,
|
|
4946
|
+
schemas: this.schemas,
|
|
4947
|
+
relationships: this.relationships,
|
|
4948
|
+
attachMethods: (name, entity) => attachMethods(this._ctx, name, entity),
|
|
4949
|
+
buildWhereClause: (conds, prefix) => buildWhereClause(conds, prefix)
|
|
4950
|
+
};
|
|
4734
4951
|
this.initializeTables();
|
|
4735
|
-
this.
|
|
4952
|
+
if (this._reactive)
|
|
4953
|
+
this.initializeChangeTracking();
|
|
4736
4954
|
this.runMigrations();
|
|
4737
4955
|
if (options.indexes)
|
|
4738
4956
|
this.createIndexes(options.indexes);
|
|
4739
4957
|
for (const entityName of Object.keys(schemas)) {
|
|
4740
4958
|
const key = entityName;
|
|
4741
4959
|
const accessor = {
|
|
4742
|
-
insert: (data) => this.
|
|
4960
|
+
insert: (data) => insert(this._ctx, entityName, data),
|
|
4961
|
+
insertMany: (rows) => insertMany(this._ctx, entityName, rows),
|
|
4743
4962
|
update: (idOrData, data) => {
|
|
4744
4963
|
if (typeof idOrData === "number")
|
|
4745
|
-
return this.
|
|
4746
|
-
return this.
|
|
4964
|
+
return update(this._ctx, entityName, idOrData, data);
|
|
4965
|
+
return createUpdateBuilder(this._ctx, entityName, idOrData);
|
|
4966
|
+
},
|
|
4967
|
+
upsert: (conditions, data) => upsert(this._ctx, entityName, data, conditions),
|
|
4968
|
+
delete: (id) => deleteEntity(this._ctx, entityName, id),
|
|
4969
|
+
select: (...cols) => createQueryBuilder(this._ctx, entityName, cols),
|
|
4970
|
+
on: (event, callback) => {
|
|
4971
|
+
return this._registerListener(entityName, event, callback);
|
|
4747
4972
|
},
|
|
4748
|
-
upsert: (conditions, data) => this.upsert(entityName, data, conditions),
|
|
4749
|
-
delete: (id) => this.delete(entityName, id),
|
|
4750
|
-
select: (...cols) => this._createQueryBuilder(entityName, cols),
|
|
4751
4973
|
_tableName: entityName
|
|
4752
4974
|
};
|
|
4753
4975
|
this[key] = accessor;
|
|
@@ -4756,44 +4978,53 @@ class _Database {
|
|
|
4756
4978
|
initializeTables() {
|
|
4757
4979
|
for (const [entityName, schema] of Object.entries(this.schemas)) {
|
|
4758
4980
|
const storableFields = getStorableFields(schema);
|
|
4759
|
-
const columnDefs = storableFields.map((f) =>
|
|
4981
|
+
const columnDefs = storableFields.map((f) => `"${f.name}" ${zodTypeToSqlType(f.type)}`);
|
|
4760
4982
|
const constraints = [];
|
|
4761
4983
|
const belongsToRels = this.relationships.filter((rel) => rel.type === "belongs-to" && rel.from === entityName);
|
|
4762
4984
|
for (const rel of belongsToRels) {
|
|
4763
|
-
constraints.push(`FOREIGN KEY (${rel.foreignKey}) REFERENCES ${rel.to}(id) ON DELETE SET NULL`);
|
|
4985
|
+
constraints.push(`FOREIGN KEY ("${rel.foreignKey}") REFERENCES "${rel.to}"(id) ON DELETE SET NULL`);
|
|
4764
4986
|
}
|
|
4765
4987
|
const allCols = columnDefs.join(", ");
|
|
4766
4988
|
const allConstraints = constraints.length > 0 ? ", " + constraints.join(", ") : "";
|
|
4767
|
-
this.db.run(`CREATE TABLE IF NOT EXISTS ${entityName} (id INTEGER PRIMARY KEY AUTOINCREMENT, ${allCols}${allConstraints})`);
|
|
4989
|
+
this.db.run(`CREATE TABLE IF NOT EXISTS "${entityName}" (id INTEGER PRIMARY KEY AUTOINCREMENT, ${allCols}${allConstraints})`);
|
|
4768
4990
|
}
|
|
4769
4991
|
}
|
|
4770
4992
|
initializeChangeTracking() {
|
|
4771
|
-
this.db.run(`CREATE TABLE IF NOT EXISTS
|
|
4772
|
-
|
|
4773
|
-
|
|
4993
|
+
this.db.run(`CREATE TABLE IF NOT EXISTS "_changes" (
|
|
4994
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
4995
|
+
tbl TEXT NOT NULL,
|
|
4996
|
+
op TEXT NOT NULL,
|
|
4997
|
+
row_id INTEGER NOT NULL
|
|
4774
4998
|
)`);
|
|
4775
4999
|
for (const entityName of Object.keys(this.schemas)) {
|
|
4776
|
-
this.db.run(`
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
5000
|
+
this.db.run(`CREATE TRIGGER IF NOT EXISTS "_trg_${entityName}_insert"
|
|
5001
|
+
AFTER INSERT ON "${entityName}"
|
|
5002
|
+
BEGIN
|
|
5003
|
+
INSERT INTO "_changes" (tbl, op, row_id) VALUES ('${entityName}', 'insert', NEW.id);
|
|
5004
|
+
END`);
|
|
5005
|
+
this.db.run(`CREATE TRIGGER IF NOT EXISTS "_trg_${entityName}_update"
|
|
5006
|
+
AFTER UPDATE ON "${entityName}"
|
|
5007
|
+
BEGIN
|
|
5008
|
+
INSERT INTO "_changes" (tbl, op, row_id) VALUES ('${entityName}', 'update', NEW.id);
|
|
5009
|
+
END`);
|
|
5010
|
+
this.db.run(`CREATE TRIGGER IF NOT EXISTS "_trg_${entityName}_delete"
|
|
5011
|
+
AFTER DELETE ON "${entityName}"
|
|
5012
|
+
BEGIN
|
|
5013
|
+
INSERT INTO "_changes" (tbl, op, row_id) VALUES ('${entityName}', 'delete', OLD.id);
|
|
5014
|
+
END`);
|
|
5015
|
+
}
|
|
5016
|
+
const row = this.db.query('SELECT MAX(id) as maxId FROM "_changes"').get();
|
|
5017
|
+
this._changeWatermark = row?.maxId ?? 0;
|
|
4787
5018
|
}
|
|
4788
5019
|
runMigrations() {
|
|
4789
5020
|
for (const [entityName, schema] of Object.entries(this.schemas)) {
|
|
4790
|
-
const existingColumns = this.db.query(`PRAGMA table_info(${entityName})`).all();
|
|
5021
|
+
const existingColumns = this.db.query(`PRAGMA table_info("${entityName}")`).all();
|
|
4791
5022
|
const existingNames = new Set(existingColumns.map((c) => c.name));
|
|
4792
5023
|
const storableFields = getStorableFields(schema);
|
|
4793
5024
|
for (const field of storableFields) {
|
|
4794
5025
|
if (!existingNames.has(field.name)) {
|
|
4795
5026
|
const sqlType = zodTypeToSqlType(field.type);
|
|
4796
|
-
this.db.run(`ALTER TABLE ${entityName} ADD COLUMN ${field.name} ${sqlType}`);
|
|
5027
|
+
this.db.run(`ALTER TABLE "${entityName}" ADD COLUMN "${field.name}" ${sqlType}`);
|
|
4797
5028
|
}
|
|
4798
5029
|
}
|
|
4799
5030
|
}
|
|
@@ -4803,278 +5034,73 @@ class _Database {
|
|
|
4803
5034
|
for (const def of indexDefs) {
|
|
4804
5035
|
const cols = Array.isArray(def) ? def : [def];
|
|
4805
5036
|
const idxName = `idx_${tableName}_${cols.join("_")}`;
|
|
4806
|
-
this.db.run(`CREATE INDEX IF NOT EXISTS ${idxName} ON ${tableName} (${cols.join(", ")})`);
|
|
5037
|
+
this.db.run(`CREATE INDEX IF NOT EXISTS "${idxName}" ON "${tableName}" (${cols.map((c) => `"${c}"`).join(", ")})`);
|
|
4807
5038
|
}
|
|
4808
5039
|
}
|
|
4809
5040
|
}
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
const
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
return
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
const columns = Object.keys(transformed);
|
|
4824
|
-
const sql = columns.length === 0 ? `INSERT INTO ${entityName} DEFAULT VALUES` : `INSERT INTO ${entityName} (${columns.join(", ")}) VALUES (${columns.map(() => "?").join(", ")})`;
|
|
4825
|
-
const result = this.db.query(sql).run(...Object.values(transformed));
|
|
4826
|
-
const newEntity = this._getById(entityName, result.lastInsertRowid);
|
|
4827
|
-
if (!newEntity)
|
|
4828
|
-
throw new Error("Failed to retrieve entity after insertion");
|
|
4829
|
-
this._bumpRevision(entityName);
|
|
4830
|
-
return newEntity;
|
|
4831
|
-
}
|
|
4832
|
-
_getById(entityName, id) {
|
|
4833
|
-
const row = this.db.query(`SELECT * FROM ${entityName} WHERE id = ?`).get(id);
|
|
4834
|
-
if (!row)
|
|
4835
|
-
return null;
|
|
4836
|
-
return this._attachMethods(entityName, transformFromStorage(row, this.schemas[entityName]));
|
|
4837
|
-
}
|
|
4838
|
-
_getOne(entityName, conditions) {
|
|
4839
|
-
const { clause, values } = this.buildWhereClause(conditions);
|
|
4840
|
-
const row = this.db.query(`SELECT * FROM ${entityName} ${clause} LIMIT 1`).get(...values);
|
|
4841
|
-
if (!row)
|
|
4842
|
-
return null;
|
|
4843
|
-
return this._attachMethods(entityName, transformFromStorage(row, this.schemas[entityName]));
|
|
4844
|
-
}
|
|
4845
|
-
_findMany(entityName, conditions = {}) {
|
|
4846
|
-
const { clause, values } = this.buildWhereClause(conditions);
|
|
4847
|
-
const rows = this.db.query(`SELECT * FROM ${entityName} ${clause}`).all(...values);
|
|
4848
|
-
return rows.map((row) => this._attachMethods(entityName, transformFromStorage(row, this.schemas[entityName])));
|
|
4849
|
-
}
|
|
4850
|
-
update(entityName, id, data) {
|
|
4851
|
-
const schema = this.schemas[entityName];
|
|
4852
|
-
const validatedData = asZodObject(schema).partial().parse(data);
|
|
4853
|
-
const transformed = transformForStorage(validatedData);
|
|
4854
|
-
if (Object.keys(transformed).length === 0)
|
|
4855
|
-
return this._getById(entityName, id);
|
|
4856
|
-
const setClause = Object.keys(transformed).map((key) => `${key} = ?`).join(", ");
|
|
4857
|
-
this.db.query(`UPDATE ${entityName} SET ${setClause} WHERE id = ?`).run(...Object.values(transformed), id);
|
|
4858
|
-
this._bumpRevision(entityName);
|
|
4859
|
-
const updatedEntity = this._getById(entityName, id);
|
|
4860
|
-
return updatedEntity;
|
|
4861
|
-
}
|
|
4862
|
-
_updateWhere(entityName, data, conditions) {
|
|
4863
|
-
const schema = this.schemas[entityName];
|
|
4864
|
-
const validatedData = asZodObject(schema).partial().parse(data);
|
|
4865
|
-
const transformed = transformForStorage(validatedData);
|
|
4866
|
-
if (Object.keys(transformed).length === 0)
|
|
4867
|
-
return 0;
|
|
4868
|
-
const { clause, values: whereValues } = this.buildWhereClause(conditions);
|
|
4869
|
-
if (!clause)
|
|
4870
|
-
throw new Error("update().where() requires at least one condition");
|
|
4871
|
-
const setCols = Object.keys(transformed);
|
|
4872
|
-
const setClause = setCols.map((key) => `${key} = ?`).join(", ");
|
|
4873
|
-
const result = this.db.query(`UPDATE ${entityName} SET ${setClause} ${clause}`).run(...setCols.map((key) => transformed[key]), ...whereValues);
|
|
4874
|
-
const affected = result.changes ?? 0;
|
|
4875
|
-
if (affected > 0)
|
|
4876
|
-
this._bumpRevision(entityName);
|
|
4877
|
-
return affected;
|
|
4878
|
-
}
|
|
4879
|
-
_createUpdateBuilder(entityName, data) {
|
|
4880
|
-
let _conditions = {};
|
|
4881
|
-
const builder = {
|
|
4882
|
-
where: (conditions) => {
|
|
4883
|
-
_conditions = { ..._conditions, ...conditions };
|
|
4884
|
-
return builder;
|
|
4885
|
-
},
|
|
4886
|
-
exec: () => this._updateWhere(entityName, data, _conditions)
|
|
5041
|
+
_registerListener(table, event, callback) {
|
|
5042
|
+
if (!this._reactive) {
|
|
5043
|
+
throw new Error("Change listeners are disabled. Set { reactive: true } (or omit it) in Database options to enable .on().");
|
|
5044
|
+
}
|
|
5045
|
+
const listener = { table, event, callback };
|
|
5046
|
+
this._listeners.push(listener);
|
|
5047
|
+
this._startPolling();
|
|
5048
|
+
return () => {
|
|
5049
|
+
const idx = this._listeners.indexOf(listener);
|
|
5050
|
+
if (idx >= 0)
|
|
5051
|
+
this._listeners.splice(idx, 1);
|
|
5052
|
+
if (this._listeners.length === 0)
|
|
5053
|
+
this._stopPolling();
|
|
4887
5054
|
};
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
const insertData = { ...conditions ?? {}, ...data ?? {} };
|
|
4899
|
-
delete insertData.id;
|
|
4900
|
-
return this.insert(entityName, insertData);
|
|
4901
|
-
}
|
|
4902
|
-
delete(entityName, id) {
|
|
4903
|
-
const entity = this._getById(entityName, id);
|
|
4904
|
-
if (entity) {
|
|
4905
|
-
this.db.query(`DELETE FROM ${entityName} WHERE id = ?`).run(id);
|
|
4906
|
-
this._bumpRevision(entityName);
|
|
4907
|
-
}
|
|
4908
|
-
}
|
|
4909
|
-
_attachMethods(entityName, entity) {
|
|
4910
|
-
const augmented = entity;
|
|
4911
|
-
augmented.update = (data) => this.update(entityName, entity.id, data);
|
|
4912
|
-
augmented.delete = () => this.delete(entityName, entity.id);
|
|
4913
|
-
for (const rel of this.relationships) {
|
|
4914
|
-
if (rel.from === entityName && rel.type === "belongs-to") {
|
|
4915
|
-
augmented[rel.relationshipField] = () => {
|
|
4916
|
-
const fkValue = entity[rel.foreignKey];
|
|
4917
|
-
return fkValue ? this._getById(rel.to, fkValue) : null;
|
|
4918
|
-
};
|
|
4919
|
-
} else if (rel.from === entityName && rel.type === "one-to-many") {
|
|
4920
|
-
const belongsToRel = this.relationships.find((r) => r.type === "belongs-to" && r.from === rel.to && r.to === rel.from);
|
|
4921
|
-
if (belongsToRel) {
|
|
4922
|
-
const fk = belongsToRel.foreignKey;
|
|
4923
|
-
augmented[rel.relationshipField] = () => {
|
|
4924
|
-
return this._findMany(rel.to, { [fk]: entity.id });
|
|
4925
|
-
};
|
|
4926
|
-
}
|
|
4927
|
-
}
|
|
5055
|
+
}
|
|
5056
|
+
_startPolling() {
|
|
5057
|
+
if (this._pollTimer)
|
|
5058
|
+
return;
|
|
5059
|
+
this._pollTimer = setInterval(() => this._processChanges(), this._pollInterval);
|
|
5060
|
+
}
|
|
5061
|
+
_stopPolling() {
|
|
5062
|
+
if (this._pollTimer) {
|
|
5063
|
+
clearInterval(this._pollTimer);
|
|
5064
|
+
this._pollTimer = null;
|
|
4928
5065
|
}
|
|
4929
|
-
const storableFieldNames = new Set(getStorableFields(this.schemas[entityName]).map((f) => f.name));
|
|
4930
|
-
return new Proxy(augmented, {
|
|
4931
|
-
set: (target, prop, value) => {
|
|
4932
|
-
if (storableFieldNames.has(prop) && target[prop] !== value) {
|
|
4933
|
-
this.update(entityName, target.id, { [prop]: value });
|
|
4934
|
-
}
|
|
4935
|
-
target[prop] = value;
|
|
4936
|
-
return true;
|
|
4937
|
-
},
|
|
4938
|
-
get: (target, prop, receiver) => Reflect.get(target, prop, receiver)
|
|
4939
|
-
});
|
|
4940
5066
|
}
|
|
4941
|
-
|
|
4942
|
-
const
|
|
4943
|
-
const
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
5067
|
+
_processChanges() {
|
|
5068
|
+
const head = this.db.query('SELECT MAX(id) as m FROM "_changes"').get();
|
|
5069
|
+
const maxId = head?.m ?? 0;
|
|
5070
|
+
if (maxId <= this._changeWatermark)
|
|
5071
|
+
return;
|
|
5072
|
+
const changes = this.db.query('SELECT id, tbl, op, row_id FROM "_changes" WHERE id > ? ORDER BY id').all(this._changeWatermark);
|
|
5073
|
+
for (const change of changes) {
|
|
5074
|
+
const listeners = this._listeners.filter((l) => l.table === change.tbl && l.event === change.op);
|
|
5075
|
+
if (listeners.length > 0) {
|
|
5076
|
+
if (change.op === "delete") {
|
|
5077
|
+
const payload = { id: change.row_id };
|
|
5078
|
+
for (const l of listeners) {
|
|
5079
|
+
try {
|
|
5080
|
+
l.callback(payload);
|
|
5081
|
+
} catch {}
|
|
4955
5082
|
}
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
const operator = Object.keys(value)[0];
|
|
4965
|
-
if (!operator?.startsWith("$")) {
|
|
4966
|
-
throw new Error(`Querying on nested object '${key}' not supported. Use operators like $gt.`);
|
|
4967
|
-
}
|
|
4968
|
-
const operand = value[operator];
|
|
4969
|
-
if (operator === "$in") {
|
|
4970
|
-
if (!Array.isArray(operand))
|
|
4971
|
-
throw new Error(`$in for '${key}' requires an array`);
|
|
4972
|
-
if (operand.length === 0) {
|
|
4973
|
-
parts.push("1 = 0");
|
|
4974
|
-
continue;
|
|
5083
|
+
} else {
|
|
5084
|
+
const row = getById(this._ctx, change.tbl, change.row_id);
|
|
5085
|
+
if (row) {
|
|
5086
|
+
for (const l of listeners) {
|
|
5087
|
+
try {
|
|
5088
|
+
l.callback(row);
|
|
5089
|
+
} catch {}
|
|
5090
|
+
}
|
|
4975
5091
|
}
|
|
4976
|
-
parts.push(`${fieldName} IN (${operand.map(() => "?").join(", ")})`);
|
|
4977
|
-
values.push(...operand.map((v) => transformForStorage({ v }).v));
|
|
4978
|
-
continue;
|
|
4979
5092
|
}
|
|
4980
|
-
const sqlOp = { $gt: ">", $gte: ">=", $lt: "<", $lte: "<=", $ne: "!=" }[operator];
|
|
4981
|
-
if (!sqlOp)
|
|
4982
|
-
throw new Error(`Unsupported operator '${operator}' on '${key}'`);
|
|
4983
|
-
parts.push(`${fieldName} ${sqlOp} ?`);
|
|
4984
|
-
values.push(transformForStorage({ operand }).operand);
|
|
4985
|
-
} else {
|
|
4986
|
-
parts.push(`${fieldName} = ?`);
|
|
4987
|
-
values.push(transformForStorage({ value }).value);
|
|
4988
5093
|
}
|
|
5094
|
+
this._changeWatermark = change.id;
|
|
4989
5095
|
}
|
|
4990
|
-
|
|
5096
|
+
this.db.run('DELETE FROM "_changes" WHERE id <= ?', this._changeWatermark);
|
|
4991
5097
|
}
|
|
4992
5098
|
transaction(callback) {
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
} catch (error) {
|
|
4999
|
-
this.db.run("ROLLBACK");
|
|
5000
|
-
throw new Error(`Transaction failed: ${error.message}`);
|
|
5001
|
-
}
|
|
5002
|
-
}
|
|
5003
|
-
_createQueryBuilder(entityName, initialCols) {
|
|
5004
|
-
const schema = this.schemas[entityName];
|
|
5005
|
-
const executor = (sql, params, raw) => {
|
|
5006
|
-
const rows = this.db.query(sql).all(...params);
|
|
5007
|
-
if (raw)
|
|
5008
|
-
return rows;
|
|
5009
|
-
return rows.map((row) => this._attachMethods(entityName, transformFromStorage(row, schema)));
|
|
5010
|
-
};
|
|
5011
|
-
const singleExecutor = (sql, params, raw) => {
|
|
5012
|
-
const results = executor(sql, params, raw);
|
|
5013
|
-
return results.length > 0 ? results[0] : null;
|
|
5014
|
-
};
|
|
5015
|
-
const joinResolver = (fromTable, toTable) => {
|
|
5016
|
-
const belongsTo = this.relationships.find((r) => r.type === "belongs-to" && r.from === fromTable && r.to === toTable);
|
|
5017
|
-
if (belongsTo)
|
|
5018
|
-
return { fk: belongsTo.foreignKey, pk: "id" };
|
|
5019
|
-
const reverse = this.relationships.find((r) => r.type === "belongs-to" && r.from === toTable && r.to === fromTable);
|
|
5020
|
-
if (reverse)
|
|
5021
|
-
return { fk: "id", pk: reverse.foreignKey };
|
|
5022
|
-
return null;
|
|
5023
|
-
};
|
|
5024
|
-
const revisionGetter = () => this._getRevision(entityName);
|
|
5025
|
-
const conditionResolver = (conditions) => {
|
|
5026
|
-
const resolved = {};
|
|
5027
|
-
for (const [key, value] of Object.entries(conditions)) {
|
|
5028
|
-
if (value && typeof value === "object" && typeof value.id === "number" && typeof value.delete === "function") {
|
|
5029
|
-
const fkCol = key + "_id";
|
|
5030
|
-
const rel = this.relationships.find((r) => r.type === "belongs-to" && r.from === entityName && r.foreignKey === fkCol);
|
|
5031
|
-
if (rel) {
|
|
5032
|
-
resolved[fkCol] = value.id;
|
|
5033
|
-
} else {
|
|
5034
|
-
const relByNav = this.relationships.find((r) => r.type === "belongs-to" && r.from === entityName && r.to === key + "s") || this.relationships.find((r) => r.type === "belongs-to" && r.from === entityName && r.to === key);
|
|
5035
|
-
if (relByNav) {
|
|
5036
|
-
resolved[relByNav.foreignKey] = value.id;
|
|
5037
|
-
} else {
|
|
5038
|
-
resolved[key] = value;
|
|
5039
|
-
}
|
|
5040
|
-
}
|
|
5041
|
-
} else {
|
|
5042
|
-
resolved[key] = value;
|
|
5043
|
-
}
|
|
5044
|
-
}
|
|
5045
|
-
return resolved;
|
|
5046
|
-
};
|
|
5047
|
-
const eagerLoader = (parentTable, relation, parentIds) => {
|
|
5048
|
-
const hasMany = this.relationships.find((r) => r.type === "one-to-many" && r.from === parentTable && r.relationshipField === relation);
|
|
5049
|
-
if (hasMany) {
|
|
5050
|
-
const belongsTo2 = this.relationships.find((r) => r.type === "belongs-to" && r.from === hasMany.to && r.to === parentTable);
|
|
5051
|
-
if (belongsTo2) {
|
|
5052
|
-
const fk = belongsTo2.foreignKey;
|
|
5053
|
-
const placeholders = parentIds.map(() => "?").join(", ");
|
|
5054
|
-
const childRows = this.db.query(`SELECT * FROM ${hasMany.to} WHERE ${fk} IN (${placeholders})`).all(...parentIds);
|
|
5055
|
-
const groups = new Map;
|
|
5056
|
-
const childSchema = this.schemas[hasMany.to];
|
|
5057
|
-
for (const rawRow of childRows) {
|
|
5058
|
-
const entity = this._attachMethods(hasMany.to, transformFromStorage(rawRow, childSchema));
|
|
5059
|
-
const parentId = rawRow[fk];
|
|
5060
|
-
if (!groups.has(parentId))
|
|
5061
|
-
groups.set(parentId, []);
|
|
5062
|
-
groups.get(parentId).push(entity);
|
|
5063
|
-
}
|
|
5064
|
-
return { key: relation, groups };
|
|
5065
|
-
}
|
|
5066
|
-
}
|
|
5067
|
-
const belongsTo = this.relationships.find((r) => r.type === "belongs-to" && r.from === parentTable && r.relationshipField === relation);
|
|
5068
|
-
if (belongsTo) {
|
|
5069
|
-
const fkValues = [...new Set(parentIds)];
|
|
5070
|
-
return null;
|
|
5071
|
-
}
|
|
5072
|
-
return null;
|
|
5073
|
-
};
|
|
5074
|
-
const builder = new QueryBuilder(entityName, executor, singleExecutor, joinResolver, conditionResolver, revisionGetter, eagerLoader, this.pollInterval);
|
|
5075
|
-
if (initialCols.length > 0)
|
|
5076
|
-
builder.select(...initialCols);
|
|
5077
|
-
return builder;
|
|
5099
|
+
return this.db.transaction(callback)();
|
|
5100
|
+
}
|
|
5101
|
+
close() {
|
|
5102
|
+
this._stopPolling();
|
|
5103
|
+
this.db.close();
|
|
5078
5104
|
}
|
|
5079
5105
|
query(callback) {
|
|
5080
5106
|
return executeProxyQuery(this.schemas, callback, (sql, params) => this.db.query(sql).all(...params));
|
|
@@ -5087,6 +5113,7 @@ export {
|
|
|
5087
5113
|
op,
|
|
5088
5114
|
createFunctionProxy,
|
|
5089
5115
|
createColumnProxy,
|
|
5116
|
+
compileIQO,
|
|
5090
5117
|
compileAST,
|
|
5091
5118
|
QueryBuilder,
|
|
5092
5119
|
Database,
|