semola 0.5.4 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +18 -45
  2. package/dist/chunk-CKQMccvm.cjs +28 -0
  3. package/dist/lib/api/index.cjs +29 -15
  4. package/dist/lib/api/index.mjs +30 -16
  5. package/dist/lib/cache/index.cjs +47 -22
  6. package/dist/lib/cache/index.d.cts +3 -24
  7. package/dist/lib/cache/index.d.mts +3 -24
  8. package/dist/lib/cache/index.mjs +48 -23
  9. package/dist/lib/cron/index.cjs +117 -117
  10. package/dist/lib/cron/index.mjs +118 -118
  11. package/dist/lib/errors/index.d.cts +12 -1
  12. package/dist/lib/errors/index.d.mts +12 -1
  13. package/dist/lib/logging/index.cjs +1 -0
  14. package/dist/lib/orm/index.cjs +1642 -0
  15. package/dist/lib/orm/index.d.cts +402 -0
  16. package/dist/lib/orm/index.d.mts +402 -0
  17. package/dist/lib/orm/index.mjs +1630 -0
  18. package/dist/lib/prompts/index.cjs +89 -89
  19. package/dist/lib/prompts/index.d.cts +12 -33
  20. package/dist/lib/prompts/index.d.mts +12 -33
  21. package/dist/lib/prompts/index.mjs +89 -90
  22. package/dist/lib/pubsub/index.cjs +43 -19
  23. package/dist/lib/pubsub/index.d.cts +3 -18
  24. package/dist/lib/pubsub/index.d.mts +3 -18
  25. package/dist/lib/pubsub/index.mjs +44 -20
  26. package/dist/lib/queue/index.cjs +40 -10
  27. package/dist/lib/queue/index.d.cts +11 -4
  28. package/dist/lib/queue/index.d.mts +11 -4
  29. package/dist/lib/queue/index.mjs +39 -11
  30. package/dist/lib/workflow/index.cjs +285 -282
  31. package/dist/lib/workflow/index.d.cts +76 -11
  32. package/dist/lib/workflow/index.d.mts +76 -11
  33. package/dist/lib/workflow/index.mjs +278 -284
  34. package/package.json +11 -1
  35. package/dist/index-BhGNDjPq.d.mts +0 -13
  36. package/dist/index-DxSbeGP-.d.cts +0 -13
@@ -0,0 +1,1630 @@
1
+ //#region src/lib/orm/column/index.ts
2
+ const createColumnBuilder = (column) => {
3
+ const primaryKey = () => {
4
+ return createColumnBuilder({
5
+ ...column,
6
+ _meta: {
7
+ ...column._meta,
8
+ isNullable: false,
9
+ isPrimaryKey: true
10
+ }
11
+ });
12
+ };
13
+ const notNull = () => {
14
+ return createColumnBuilder({
15
+ ...column,
16
+ _meta: {
17
+ ...column._meta,
18
+ isNullable: false
19
+ }
20
+ });
21
+ };
22
+ const nullable = (() => {
23
+ if (column._meta.isPrimaryKey) return createColumnBuilder({
24
+ ...column,
25
+ _meta: {
26
+ ...column._meta,
27
+ isNullable: false
28
+ }
29
+ });
30
+ return createColumnBuilder({
31
+ ...column,
32
+ _meta: {
33
+ ...column._meta,
34
+ isNullable: true
35
+ }
36
+ });
37
+ });
38
+ const unique = () => {
39
+ return createColumnBuilder({
40
+ ...column,
41
+ _meta: {
42
+ ...column._meta,
43
+ isUnique: true
44
+ }
45
+ });
46
+ };
47
+ const defaultHandler = (value) => {
48
+ return createColumnBuilder({
49
+ ...column,
50
+ _meta: {
51
+ ...column._meta,
52
+ hasDefault: true
53
+ },
54
+ _default: value
55
+ });
56
+ };
57
+ const referencesBuilder = (tableColumn) => {
58
+ return createColumnBuilder({
59
+ ...column,
60
+ references: { tableColumn }
61
+ });
62
+ };
63
+ const references = referencesBuilder;
64
+ references.tableColumn = column.references?.tableColumn;
65
+ return {
66
+ ...column,
67
+ primaryKey,
68
+ notNull,
69
+ nullable,
70
+ unique,
71
+ default: defaultHandler,
72
+ references
73
+ };
74
+ };
75
+ const createBaseColumn = (sqlName, type, enumValues) => {
76
+ return createColumnBuilder({
77
+ sqlName,
78
+ type,
79
+ enumValues,
80
+ _meta: {
81
+ isNullable: true,
82
+ isPrimaryKey: false,
83
+ isUnique: false,
84
+ hasDefault: false
85
+ }
86
+ });
87
+ };
88
+ const string = (sqlName) => {
89
+ return createBaseColumn(sqlName, "string");
90
+ };
91
+ const uuid = (sqlName) => {
92
+ return string(sqlName);
93
+ };
94
+ const number = (sqlName) => {
95
+ return createBaseColumn(sqlName, "number");
96
+ };
97
+ const boolean = (sqlName) => {
98
+ return createBaseColumn(sqlName, "boolean");
99
+ };
100
+ const date = (sqlName) => {
101
+ return createBaseColumn(sqlName, "date");
102
+ };
103
+ const enumType = (sqlName, values) => {
104
+ return createBaseColumn(sqlName, "enum", values);
105
+ };
106
+ const json = (sqlName) => {
107
+ return createBaseColumn(sqlName, "json");
108
+ };
109
+ const jsonb = (sqlName) => {
110
+ return createBaseColumn(sqlName, "jsonb");
111
+ };
112
+ //#endregion
113
+ //#region src/lib/orm/utils.ts
114
+ const quoteIdentifier = (identifier) => {
115
+ identifier = identifier.replaceAll("\"", "\"\"");
116
+ return `"${identifier}"`;
117
+ };
118
+ //#endregion
119
+ //#region src/lib/orm/dialect/relation-fk.ts
120
+ const resolveHasManyForeignKeyColumn = (sourceTable, targetTable) => {
121
+ const sourceColumnValues = Object.values(sourceTable.columns);
122
+ const candidates = [];
123
+ for (const [, column] of Object.entries(targetTable.columns)) {
124
+ if (!column.references) continue;
125
+ const getReferencedColumn = column.references.tableColumn;
126
+ if (!getReferencedColumn) continue;
127
+ const referencedColumn = getReferencedColumn();
128
+ if (sourceColumnValues.some((sourceCol) => {
129
+ return sourceCol === referencedColumn;
130
+ })) candidates.push({
131
+ fk: column,
132
+ source: referencedColumn
133
+ });
134
+ }
135
+ if (candidates.length > 1) throw new Error(`Ambiguous hasMany foreign key from ${targetTable.sqlName} to ${sourceTable.sqlName}`);
136
+ const [candidate] = candidates;
137
+ if (!candidate) throw new Error(`Missing hasMany foreign key from ${targetTable.sqlName} to ${sourceTable.sqlName}`);
138
+ return candidate;
139
+ };
140
+ const resolveHasOneForeignKeyColumn = (input) => {
141
+ const { sourceTable, relationTable, relationForeignKey } = input;
142
+ const localForeignKey = sourceTable.columns[relationForeignKey];
143
+ if (!localForeignKey) throw new Error(`Missing hasOne foreign key column ${relationForeignKey} on ${sourceTable.sqlName}`);
144
+ if (!localForeignKey.references?.tableColumn) throw new Error(`Column ${relationForeignKey} on ${sourceTable.sqlName} is not a foreign key - call .references() on it`);
145
+ const referencedColumn = localForeignKey.references.tableColumn();
146
+ if (!Object.values(relationTable.columns).some((column) => {
147
+ return column === referencedColumn;
148
+ })) throw new Error(`Column ${relationForeignKey} on ${sourceTable.sqlName} does not reference ${relationTable.sqlName}`);
149
+ return {
150
+ localForeignKey,
151
+ target: referencedColumn
152
+ };
153
+ };
154
+ //#endregion
155
+ //#region src/lib/orm/dialect/clauses.ts
156
+ const FALSE_WHERE_SQL = "(1 = 0)";
157
+ const TRUE_WHERE_SQL = "(1 = 1)";
158
+ const RELATION_FILTER_KEYS = [
159
+ "every",
160
+ "some",
161
+ "none"
162
+ ];
163
+ const EMPTY_INCLUDE = {
164
+ sql: "",
165
+ params: [],
166
+ descriptors: []
167
+ };
168
+ const serializeParam = (value) => {
169
+ if (value instanceof Date) return value.toISOString();
170
+ return value;
171
+ };
172
+ const escapeLikeValue = (value) => {
173
+ return serializeParam(`${serializeParam(value)}`.replaceAll("\\", "\\\\").replaceAll("%", "\\%").replaceAll("_", "\\_"));
174
+ };
175
+ const OPERATORS = {
176
+ equals: {
177
+ sql: (ph) => `= ${ph}`,
178
+ transform: (v) => serializeParam(v)
179
+ },
180
+ gt: {
181
+ sql: (ph) => `> ${ph}`,
182
+ transform: (v) => serializeParam(v)
183
+ },
184
+ gte: {
185
+ sql: (ph) => `>= ${ph}`,
186
+ transform: (v) => serializeParam(v)
187
+ },
188
+ lt: {
189
+ sql: (ph) => `< ${ph}`,
190
+ transform: (v) => serializeParam(v)
191
+ },
192
+ lte: {
193
+ sql: (ph) => `<= ${ph}`,
194
+ transform: (v) => serializeParam(v)
195
+ },
196
+ startsWith: {
197
+ sql: (ph) => `LIKE ${ph} ESCAPE '\\'`,
198
+ transform: (v) => serializeParam(`${escapeLikeValue(v)}%`)
199
+ },
200
+ endsWith: {
201
+ sql: (ph) => `LIKE ${ph} ESCAPE '\\'`,
202
+ transform: (v) => serializeParam(`%${escapeLikeValue(v)}`)
203
+ },
204
+ contains: {
205
+ sql: (ph) => `LIKE ${ph} ESCAPE '\\'`,
206
+ transform: (v) => serializeParam(`%${escapeLikeValue(v)}%`)
207
+ }
208
+ };
209
+ const createNextPlaceholder = (spec) => {
210
+ let index = 0;
211
+ return () => {
212
+ index += 1;
213
+ return spec.formatPlaceholder(index);
214
+ };
215
+ };
216
+ const buildSelectList = (columns, include) => {
217
+ if (include.sql) return `${columns}, ${include.sql}`;
218
+ return columns;
219
+ };
220
+ const serializeColumnValue = (column, value) => {
221
+ if (column.type !== "json" && column.type !== "jsonb") return serializeParam(value);
222
+ if (value === null) return value;
223
+ if (value === void 0) return null;
224
+ return JSON.stringify(value);
225
+ };
226
+ const isPlainObject = (value) => {
227
+ if (value === null) return false;
228
+ if (typeof value !== "object") return false;
229
+ if (Array.isArray(value)) return false;
230
+ if (value instanceof Date) return false;
231
+ const prototype = Object.getPrototypeOf(value);
232
+ if (prototype === null) return true;
233
+ if (prototype === Object.prototype) return true;
234
+ return false;
235
+ };
236
+ const appendDirectWhereClause = (input) => {
237
+ const { clauses, params, nextPlaceholder, column, sqlName, value } = input;
238
+ if (value === null) {
239
+ clauses.push(`${sqlName} IS NULL`);
240
+ return;
241
+ }
242
+ clauses.push(`${sqlName} = ${nextPlaceholder()}`);
243
+ params.push(serializeColumnValue(column, value));
244
+ };
245
+ const appendOperatorWhereClauses = (input) => {
246
+ const { clauses, params, nextPlaceholder, column, sqlName, jsKey, value } = input;
247
+ const entries = Object.entries(value);
248
+ if (!entries.length) throw new Error(`Missing where operator for field ${jsKey}`);
249
+ for (const [op, operand] of entries) {
250
+ if (op === "in" || op === "notIn") {
251
+ if (!Array.isArray(operand)) throw new Error(`Expected array for where operator: ${op} for field ${jsKey}`);
252
+ if (op === "in" && operand.length === 0) {
253
+ clauses.push(FALSE_WHERE_SQL);
254
+ continue;
255
+ }
256
+ if (op === "notIn" && operand.length === 0) continue;
257
+ const placeholders = operand.map(() => nextPlaceholder());
258
+ const keyword = op === "in" ? "IN" : "NOT IN";
259
+ clauses.push(`${sqlName} ${keyword} (${placeholders.join(", ")})`);
260
+ for (const item of operand) params.push(serializeColumnValue(column, item));
261
+ continue;
262
+ }
263
+ if (op === "between") {
264
+ if (!Array.isArray(operand)) throw new Error(`Expected array for where operator: ${op} for field ${jsKey}`);
265
+ if (operand.length !== 2) throw new Error(`Expected 2-element array for where operator: ${op} for field ${jsKey}`);
266
+ const [min, max] = operand;
267
+ const ph1 = nextPlaceholder();
268
+ const ph2 = nextPlaceholder();
269
+ clauses.push(`${sqlName} BETWEEN ${ph1} AND ${ph2}`);
270
+ params.push(serializeColumnValue(column, min), serializeColumnValue(column, max));
271
+ continue;
272
+ }
273
+ const operator = OPERATORS[op];
274
+ if (!operator) throw new Error(`Unknown where operator: ${op} for field ${jsKey}`);
275
+ if (op === "equals" && operand === null) {
276
+ clauses.push(`${sqlName} IS NULL`);
277
+ continue;
278
+ }
279
+ clauses.push(`${sqlName} ${operator.sql(nextPlaceholder())}`);
280
+ params.push(operator.transform(serializeColumnValue(column, operand)));
281
+ }
282
+ };
283
+ const parseRelationFilters = (relationName, value) => {
284
+ if (!isPlainObject(value)) throw new Error(`Relation where filter for ${relationName} must be an object`);
285
+ const filter = value;
286
+ const filters = [];
287
+ for (const filterKey of RELATION_FILTER_KEYS) {
288
+ if (!(filterKey in filter)) continue;
289
+ const nestedWhere = filter[filterKey];
290
+ if (!isPlainObject(nestedWhere)) throw new Error(`Relation where filter ${filterKey} must be an object`);
291
+ filters.push({
292
+ key: filterKey,
293
+ where: nestedWhere
294
+ });
295
+ }
296
+ if (filters.length === 0) throw new Error(`Relation where filter for ${relationName} must include at least one of every, some, or none`);
297
+ return filters;
298
+ };
299
+ const isRelationFilterValue = (value) => {
300
+ if (!isPlainObject(value)) return false;
301
+ const filter = value;
302
+ for (const key of RELATION_FILTER_KEYS) if (key in filter) return true;
303
+ return Object.keys(filter).length === 0;
304
+ };
305
+ const buildRelationForeignKeyCondition = (input) => {
306
+ const { parentTable, parentAlias, relation, relationTable, relationAlias } = input;
307
+ if (relation._type === "hasMany") {
308
+ const { fk: foreignKey, source: sourceColumn } = resolveHasManyForeignKeyColumn(parentTable, relationTable);
309
+ return `${relationAlias}.${quoteIdentifier(foreignKey.sqlName)} = ${parentAlias}.${quoteIdentifier(sourceColumn.sqlName)}`;
310
+ }
311
+ if (relation._type !== "hasOne") throw new Error("Expected hasOne relation");
312
+ const { localForeignKey, target } = resolveHasOneForeignKeyColumn({
313
+ sourceTable: parentTable,
314
+ relationTable,
315
+ relationForeignKey: relation._foreignKey
316
+ });
317
+ return `${relationAlias}.${quoteIdentifier(target.sqlName)} = ${parentAlias}.${quoteIdentifier(localForeignKey.sqlName)}`;
318
+ };
319
+ const appendRelationFilterClause = (input) => {
320
+ const { clauses, params, nextPlaceholder, parentAlias, table, relation, relationName, key, where: nestedWhere } = input;
321
+ if (!parentAlias) throw new Error("parentAlias is required for relation where filters");
322
+ const relationTable = relation._table;
323
+ const relationAlias = `where_${relationName}__${relationTable.sqlName}`;
324
+ const fkCondition = buildRelationForeignKeyCondition({
325
+ parentTable: table,
326
+ parentAlias,
327
+ relation,
328
+ relationTable,
329
+ relationAlias
330
+ });
331
+ const nested = buildWhereClause({
332
+ nextPlaceholder,
333
+ table: relationTable,
334
+ where: nestedWhere
335
+ });
336
+ const nestedCondition = nested.sql ? nested.sql : TRUE_WHERE_SQL;
337
+ params.push(...nested.params);
338
+ const relationFrom = `${quoteIdentifier(relationTable.sqlName)} AS ${relationAlias}`;
339
+ if (key === "some") {
340
+ clauses.push(`EXISTS (SELECT 1 FROM ${relationFrom} WHERE ${fkCondition} AND (${nestedCondition}))`);
341
+ return;
342
+ }
343
+ if (key === "none") {
344
+ clauses.push(`NOT EXISTS (SELECT 1 FROM ${relationFrom} WHERE ${fkCondition} AND (${nestedCondition}))`);
345
+ return;
346
+ }
347
+ clauses.push(`NOT EXISTS (SELECT 1 FROM ${relationFrom} WHERE ${fkCondition} AND NOT (${nestedCondition}))`);
348
+ };
349
+ const appendRelationWhereClause = (input) => {
350
+ const { parentAlias, relationName, value } = input;
351
+ if (!parentAlias) throw new Error("parentAlias is required for relation where filters");
352
+ const filters = parseRelationFilters(relationName, value);
353
+ for (const filter of filters) appendRelationFilterClause({
354
+ ...input,
355
+ ...filter,
356
+ parentAlias
357
+ });
358
+ };
359
+ const appendWhereClause = (input) => {
360
+ const { clauses, params, nextPlaceholder, table, jsKey, value } = input;
361
+ if (!(jsKey in table.columns)) throw new Error(`Unknown where key "${jsKey}" on table ${table.sqlName}`);
362
+ const column = table.columns[jsKey];
363
+ if (!column) throw new Error(`Unknown where key "${jsKey}" on table ${table.sqlName}`);
364
+ const sqlName = quoteIdentifier(column.sqlName);
365
+ if (!isPlainObject(value)) {
366
+ appendDirectWhereClause({
367
+ clauses,
368
+ params,
369
+ nextPlaceholder,
370
+ column,
371
+ sqlName,
372
+ value
373
+ });
374
+ return;
375
+ }
376
+ appendOperatorWhereClauses({
377
+ clauses,
378
+ params,
379
+ nextPlaceholder,
380
+ column,
381
+ sqlName,
382
+ jsKey,
383
+ value
384
+ });
385
+ };
386
+ const buildWhereClause = (input) => {
387
+ const { where, relations, parentAlias } = input;
388
+ const clauses = [];
389
+ const params = [];
390
+ if (!where) return {
391
+ sql: "",
392
+ params
393
+ };
394
+ for (const [jsKey, value] of Object.entries(where)) {
395
+ if (value === void 0) continue;
396
+ const logicalWhereKey = getLogicalWhereKey(jsKey);
397
+ if (logicalWhereKey) {
398
+ appendLogicalWhereClause({
399
+ ...input,
400
+ clauses,
401
+ params,
402
+ jsKey: logicalWhereKey,
403
+ value
404
+ });
405
+ continue;
406
+ }
407
+ const relation = relations?.[jsKey];
408
+ if (relation && isRelationFilterValue(value)) {
409
+ appendRelationWhereClause({
410
+ ...input,
411
+ clauses,
412
+ params,
413
+ jsKey,
414
+ value,
415
+ relation,
416
+ relationName: jsKey,
417
+ parentAlias: parentAlias ?? quoteIdentifier(input.table.sqlName)
418
+ });
419
+ continue;
420
+ }
421
+ appendWhereClause({
422
+ ...input,
423
+ clauses,
424
+ params,
425
+ jsKey,
426
+ value
427
+ });
428
+ }
429
+ return {
430
+ sql: clauses.join(` AND `),
431
+ params
432
+ };
433
+ };
434
+ const getLogicalWhereKey = (jsKey) => {
435
+ if (jsKey === "$and") return jsKey;
436
+ if (jsKey === "$not") return jsKey;
437
+ if (jsKey === "$or") return jsKey;
438
+ return null;
439
+ };
440
+ const getLogicalOperator = (jsKey) => {
441
+ if (jsKey === "$or") return "OR";
442
+ return "AND";
443
+ };
444
+ const getLogicalWhereValues = (jsKey, value) => {
445
+ if (jsKey === "$or" && !Array.isArray(value)) throw new Error("$or where value must be an array");
446
+ if (Array.isArray(value)) return value;
447
+ return [value];
448
+ };
449
+ const appendLogicalWhereClause = (input) => {
450
+ const { clauses, params, jsKey } = input;
451
+ const collected = collectLogicalWhereClauses(input);
452
+ if (typeof collected === "string") {
453
+ clauses.push(collected);
454
+ return;
455
+ }
456
+ if (!collected.nestedClauses.length) return;
457
+ params.push(...collected.nestedParams);
458
+ if (jsKey === "$not") {
459
+ const operator = "NOT";
460
+ const combinedNegatedClause = collected.nestedClauses.map((nestedClause) => `${operator} (${nestedClause})`).join(` AND `);
461
+ clauses.push(combinedNegatedClause);
462
+ return;
463
+ }
464
+ const operator = getLogicalOperator(jsKey);
465
+ clauses.push(`(${collected.nestedClauses.join(` ${operator} `)})`);
466
+ };
467
+ const collectLogicalWhereClauses = (input) => {
468
+ const { jsKey, value } = input;
469
+ const values = getLogicalWhereValues(jsKey, value);
470
+ const nestedClauses = [];
471
+ const nestedParams = [];
472
+ for (const nestedValue of values) {
473
+ if (!isPlainObject(nestedValue)) throw new Error(`${jsKey} where value must contain object filters`);
474
+ const nested = buildWhereClause({
475
+ ...input,
476
+ where: nestedValue
477
+ });
478
+ if (!nested.sql) {
479
+ if (jsKey === "$or") return TRUE_WHERE_SQL;
480
+ continue;
481
+ }
482
+ nestedClauses.push(`(${nested.sql})`);
483
+ nestedParams.push(...nested.params);
484
+ }
485
+ if (jsKey === "$or" && !nestedClauses.length) return FALSE_WHERE_SQL;
486
+ return {
487
+ nestedClauses,
488
+ nestedParams
489
+ };
490
+ };
491
+ const getColumnAlias = (sqlName, jsKey) => {
492
+ return `${quoteIdentifier(sqlName)} AS ${quoteIdentifier(jsKey)}`;
493
+ };
494
+ const buildSelectColumns = (table, select) => {
495
+ if (!select || Object.keys(select).length === 0) return Object.entries(table.columns).map(([key, column]) => getColumnAlias(column.sqlName, key)).join(", ");
496
+ const selectedColumns = [];
497
+ const keys = Object.keys(select);
498
+ for (const key of keys) {
499
+ const column = table.columns[key];
500
+ if (!column) throw new Error(`Unknown select key "${key}" on table ${table.sqlName}`);
501
+ selectedColumns.push(getColumnAlias(column.sqlName, key));
502
+ }
503
+ return selectedColumns.join(", ");
504
+ };
505
+ const buildOrderByClause = (table, orderBy) => {
506
+ if (!orderBy) return "";
507
+ const clauses = [];
508
+ for (const [jsKey, direction] of Object.entries(orderBy)) {
509
+ const column = table.columns[jsKey];
510
+ if (!column) throw new Error(`Unknown orderBy key "${jsKey}" on table ${table.sqlName}`);
511
+ if (direction === "desc") {
512
+ clauses.push(`${quoteIdentifier(column.sqlName)} DESC`);
513
+ continue;
514
+ }
515
+ if (direction === "asc") {
516
+ clauses.push(`${quoteIdentifier(column.sqlName)} ASC`);
517
+ continue;
518
+ }
519
+ throw new Error(`Unknown orderBy direction "${direction}" for key "${jsKey}" on table ${table.sqlName}`);
520
+ }
521
+ if (!clauses.length) return "";
522
+ return clauses.join(", ");
523
+ };
524
+ const buildPaginationClause = (input) => {
525
+ const { spec, nextPlaceholder, take, skip } = input;
526
+ const params = [];
527
+ if (take === void 0) {
528
+ if (skip === void 0) return {
529
+ sql: "",
530
+ params
531
+ };
532
+ const skipPh = nextPlaceholder();
533
+ params.push(skip);
534
+ return {
535
+ sql: `${spec.unlimitedOffsetKeyword} ${skipPh}`,
536
+ params
537
+ };
538
+ }
539
+ const takePh = nextPlaceholder();
540
+ params.push(take);
541
+ if (skip === void 0) return {
542
+ sql: `LIMIT ${takePh}`,
543
+ params
544
+ };
545
+ const skipPh = nextPlaceholder();
546
+ params.push(skip);
547
+ return {
548
+ sql: `LIMIT ${takePh} OFFSET ${skipPh}`,
549
+ params
550
+ };
551
+ };
552
+ const buildSelectStatement = (input) => {
553
+ const { tableName, columns, where, orderBy, pagination } = input;
554
+ let query = `SELECT ${columns} FROM ${tableName}`;
555
+ if (where) query = `${query} WHERE ${where}`;
556
+ if (orderBy) query = `${query} ORDER BY ${orderBy}`;
557
+ if (pagination) query = `${query} ${pagination}`;
558
+ return query;
559
+ };
560
+ const validateFindUniqueWhere = (table, where) => {
561
+ const entries = Object.entries(where).filter(([, value]) => value !== void 0);
562
+ if (!entries.map(([key]) => key).length) throw new Error("findUnique requires at least one where key");
563
+ let hasUniqueKey = false;
564
+ for (const [key] of entries) {
565
+ const column = table.columns[key];
566
+ if (!column) throw new Error(`Unknown where key ${key} on table ${table.sqlName}`);
567
+ if (column._meta.isPrimaryKey || column._meta.isUnique) hasUniqueKey = true;
568
+ }
569
+ if (!hasUniqueKey) throw new Error("findUnique where must include at least one unique or primary key column");
570
+ };
571
+ const resolveCreateValue = (column, provided) => {
572
+ if (provided !== void 0) return provided;
573
+ if (column._default) return column._default();
574
+ return null;
575
+ };
576
+ const buildSetClauses = (input) => {
577
+ const { nextPlaceholder, table, data } = input;
578
+ const setClauses = [];
579
+ const params = [];
580
+ for (const [jsKey, value] of Object.entries(data)) {
581
+ if (value === void 0) continue;
582
+ const column = table.columns[jsKey];
583
+ if (!column) continue;
584
+ setClauses.push(`${quoteIdentifier(column.sqlName)} = ${nextPlaceholder()}`);
585
+ params.push(serializeColumnValue(column, value));
586
+ }
587
+ return {
588
+ setClauses,
589
+ params
590
+ };
591
+ };
592
+ //#endregion
593
+ //#region src/lib/orm/dialect/relations.ts
594
+ const buildJsonObjectExpression = (input) => {
595
+ const { spec, alias, table, extraPairs = [], select } = input;
596
+ const allEntries = Object.entries(table.columns);
597
+ const hasSelect = select !== void 0 && Object.keys(select).length > 0;
598
+ let visibleEntries = allEntries;
599
+ if (hasSelect) {
600
+ for (const key of Object.keys(select)) if (!(key in table.columns)) throw new Error(`Unknown select key "${key}" on table ${table.sqlName}`);
601
+ visibleEntries = allEntries.filter(([key]) => key in select);
602
+ }
603
+ const pairs = visibleEntries.flatMap(([jsKey, column]) => [`'${jsKey}'`, `${alias}.${quoteIdentifier(column.sqlName)}`]);
604
+ return `${spec.jsonObjectFunctionName}(${[...pairs, ...extraPairs].join(", ")})`;
605
+ };
606
+ const getRelationOptions = (includeValue) => {
607
+ let options = {};
608
+ if (typeof includeValue === "object" && includeValue !== null) options = includeValue;
609
+ return options;
610
+ };
611
+ const buildNestedIncludePairs = (input) => {
612
+ const { spec, nextPlaceholder, relationTable, relationAlias, tableRelationsMap, options } = input;
613
+ const nestedRelations = tableRelationsMap.get(relationTable) ?? {};
614
+ const nestedExtraPairs = [];
615
+ const nestedParams = [];
616
+ const nestedDescriptors = [];
617
+ if (!options.include) return {
618
+ nestedExtraPairs,
619
+ nestedParams,
620
+ nestedDescriptors
621
+ };
622
+ for (const [nestedName, nestedValue] of Object.entries(options.include)) {
623
+ if (!nestedValue) continue;
624
+ const nestedRelation = nestedRelations[nestedName];
625
+ if (!nestedRelation) throw new Error(`Unknown relation ${nestedName} on table ${relationTable.sqlName}`);
626
+ const result = buildRelationSubquery({
627
+ spec,
628
+ nextPlaceholder,
629
+ parentTable: relationTable,
630
+ parentAlias: relationAlias,
631
+ relation: nestedRelation,
632
+ relationName: nestedName,
633
+ includeValue: nestedValue,
634
+ tableRelationsMap
635
+ });
636
+ nestedExtraPairs.push(`'${nestedName}'`, result.sql);
637
+ nestedParams.push(...result.params);
638
+ nestedDescriptors.push(result.descriptor);
639
+ }
640
+ return {
641
+ nestedExtraPairs,
642
+ nestedParams,
643
+ nestedDescriptors
644
+ };
645
+ };
646
+ const joinWhereSql = (fkCondition, whereSql) => {
647
+ if (whereSql) return `${fkCondition} AND ${whereSql}`;
648
+ return fkCondition;
649
+ };
650
+ const buildNestedHasManySubquery = (input) => {
651
+ const { spec, relationTable, relationAlias, whereSql, orderBy, paginationSql, jsonObj } = input;
652
+ if (!orderBy && !paginationSql) return `SELECT ${spec.jsonArrayAggregateFunctionName}(${jsonObj}) FROM ${quoteIdentifier(relationTable.sqlName)} AS ${relationAlias} WHERE ${whereSql}`;
653
+ let innerQuery = `SELECT * FROM ${quoteIdentifier(relationTable.sqlName)} AS ${relationAlias} WHERE ${whereSql}`;
654
+ if (orderBy) innerQuery = `${innerQuery} ORDER BY ${orderBy}`;
655
+ if (paginationSql) innerQuery = `${innerQuery} ${paginationSql}`;
656
+ return `SELECT ${spec.jsonArrayAggregateFunctionName}(${jsonObj}) FROM (${innerQuery}) AS ${relationAlias}`;
657
+ };
658
+ const buildHasManyRelationSubquery = (input) => {
659
+ const { spec, parentTable, parentAlias, relationTable, relationAlias, relationName, jsonObj, nestedDescriptors, whereSql, orderBy, paginationSql, allParams } = input;
660
+ const { fk: foreignKey, source: sourceColumn } = resolveHasManyForeignKeyColumn(parentTable, relationTable);
661
+ return {
662
+ sql: `COALESCE((${buildNestedHasManySubquery({
663
+ spec,
664
+ relationTable,
665
+ relationAlias,
666
+ whereSql: joinWhereSql(`${relationAlias}.${quoteIdentifier(foreignKey.sqlName)} = ${parentAlias}.${quoteIdentifier(sourceColumn.sqlName)}`, whereSql),
667
+ orderBy,
668
+ paginationSql,
669
+ jsonObj
670
+ })}), ${spec.emptyJsonArrayLiteral})`,
671
+ params: allParams,
672
+ descriptor: {
673
+ name: relationName,
674
+ type: "hasMany",
675
+ table: relationTable,
676
+ nested: nestedDescriptors
677
+ }
678
+ };
679
+ };
680
+ const buildHasOneRelationSubquery = (input) => {
681
+ const { parentTable, parentAlias, relation, relationTable, relationAlias, relationName, jsonObj, nestedDescriptors, whereSql, allParams } = input;
682
+ if (relation._type !== "hasOne") throw new Error(`Expected hasOne relation for ${relationName}`);
683
+ const { localForeignKey, target } = resolveHasOneForeignKeyColumn({
684
+ sourceTable: parentTable,
685
+ relationTable,
686
+ relationForeignKey: relation._foreignKey
687
+ });
688
+ const combinedWhereSql = joinWhereSql(`${relationAlias}.${quoteIdentifier(target.sqlName)} = ${parentAlias}.${quoteIdentifier(localForeignKey.sqlName)}`, whereSql);
689
+ return {
690
+ sql: `(${`SELECT ${jsonObj} FROM ${quoteIdentifier(relationTable.sqlName)} AS ${relationAlias} WHERE ${combinedWhereSql} LIMIT 1`})`,
691
+ params: allParams,
692
+ descriptor: {
693
+ name: relationName,
694
+ type: "hasOne",
695
+ table: relationTable,
696
+ nested: nestedDescriptors
697
+ }
698
+ };
699
+ };
700
+ const buildRelationSubquery = (input) => {
701
+ const { spec, nextPlaceholder, relation, relationName } = input;
702
+ const options = getRelationOptions(input.includeValue);
703
+ const relationTable = relation._table;
704
+ const relationAlias = `${relationName}__${relationTable.sqlName}`;
705
+ const { nestedExtraPairs, nestedParams, nestedDescriptors } = buildNestedIncludePairs({
706
+ ...input,
707
+ relationTable,
708
+ relationAlias,
709
+ options
710
+ });
711
+ const jsonObj = buildJsonObjectExpression({
712
+ spec,
713
+ alias: relationAlias,
714
+ table: relationTable,
715
+ extraPairs: nestedExtraPairs,
716
+ select: options.select
717
+ });
718
+ const where = buildWhereClause({
719
+ nextPlaceholder,
720
+ table: relationTable,
721
+ where: options.where
722
+ });
723
+ const orderBy = buildOrderByClause(relationTable, options.orderBy);
724
+ const pagination = buildPaginationClause({
725
+ spec,
726
+ nextPlaceholder,
727
+ take: options.take,
728
+ skip: options.skip
729
+ });
730
+ const allParams = [
731
+ ...nestedParams,
732
+ ...where.params,
733
+ ...pagination.params
734
+ ];
735
+ if (relation._type === "hasMany") return buildHasManyRelationSubquery({
736
+ ...input,
737
+ relationTable,
738
+ relationAlias,
739
+ jsonObj,
740
+ nestedDescriptors,
741
+ whereSql: where.sql,
742
+ orderBy,
743
+ paginationSql: pagination.sql,
744
+ allParams
745
+ });
746
+ return buildHasOneRelationSubquery({
747
+ ...input,
748
+ relationTable,
749
+ relationAlias,
750
+ jsonObj,
751
+ nestedDescriptors,
752
+ whereSql: where.sql,
753
+ allParams
754
+ });
755
+ };
756
+ const buildIncludeClause = (input) => {
757
+ const { spec, nextPlaceholder, table, parentAlias, relations, tableRelationsMap, include } = input;
758
+ if (!include) return EMPTY_INCLUDE;
759
+ const enabledRelations = Object.entries(include).filter(([, v]) => Boolean(v));
760
+ if (!enabledRelations.length) return EMPTY_INCLUDE;
761
+ const clauses = [];
762
+ const params = [];
763
+ const descriptors = [];
764
+ for (const [relationName, includeValue] of enabledRelations) {
765
+ const relation = relations[relationName];
766
+ if (!relation) throw new Error(`Unknown relation ${relationName} on table ${table.sqlName}`);
767
+ const result = buildRelationSubquery({
768
+ spec,
769
+ nextPlaceholder,
770
+ parentTable: table,
771
+ parentAlias,
772
+ relation,
773
+ relationName,
774
+ includeValue,
775
+ tableRelationsMap
776
+ });
777
+ clauses.push(`${result.sql} AS ${quoteIdentifier(relationName)}`);
778
+ params.push(...result.params);
779
+ descriptors.push(result.descriptor);
780
+ }
781
+ return {
782
+ sql: clauses.join(", "),
783
+ params,
784
+ descriptors
785
+ };
786
+ };
787
+ //#endregion
788
+ //#region src/lib/orm/dialect/query-builder.ts
789
+ var DialectQueryBuilder = class {
790
+ spec;
791
+ table;
792
+ relations;
793
+ tableRelationsMap;
794
+ constructor(input) {
795
+ this.spec = input.spec;
796
+ this.table = input.table;
797
+ this.relations = input.relations;
798
+ this.tableRelationsMap = input.tableRelationsMap ?? /* @__PURE__ */ new Map();
799
+ }
800
+ buildFindMany(options) {
801
+ const nextPlaceholder = createNextPlaceholder(this.spec);
802
+ const { include, where, selectColumns } = this.buildSelectIncludeWhere(nextPlaceholder, {
803
+ where: options?.where,
804
+ select: options?.select,
805
+ include: options?.include
806
+ });
807
+ const orderBy = buildOrderByClause(this.table, options?.orderBy);
808
+ const pagination = buildPaginationClause({
809
+ spec: this.spec,
810
+ nextPlaceholder,
811
+ take: options?.take,
812
+ skip: options?.skip
813
+ });
814
+ const params = [
815
+ ...include.params,
816
+ ...where.params,
817
+ ...pagination.params
818
+ ];
819
+ return {
820
+ statement: buildSelectStatement({
821
+ tableName: quoteIdentifier(this.table.sqlName),
822
+ columns: selectColumns,
823
+ where: where.sql,
824
+ orderBy,
825
+ pagination: pagination.sql
826
+ }),
827
+ params,
828
+ includeDescriptors: include.descriptors
829
+ };
830
+ }
831
+ buildFindFirst(options) {
832
+ return this.buildFindMany({
833
+ ...options,
834
+ take: 1
835
+ });
836
+ }
837
+ buildFindUnique(options) {
838
+ validateFindUniqueWhere(this.table, options.where);
839
+ const nextPlaceholder = createNextPlaceholder(this.spec);
840
+ const { include, where, selectColumns } = this.buildSelectIncludeWhere(nextPlaceholder, {
841
+ where: options.where,
842
+ select: options.select,
843
+ include: options.include
844
+ });
845
+ return {
846
+ statement: buildSelectStatement({
847
+ tableName: quoteIdentifier(this.table.sqlName),
848
+ columns: selectColumns,
849
+ where: where.sql,
850
+ orderBy: "",
851
+ pagination: "LIMIT 1"
852
+ }),
853
+ params: [...include.params, ...where.params],
854
+ includeDescriptors: include.descriptors
855
+ };
856
+ }
857
+ buildCreate(options) {
858
+ const nextPlaceholder = createNextPlaceholder(this.spec);
859
+ const provided = new Map(Object.entries(options.data));
860
+ const sqlNames = [];
861
+ const placeholders = [];
862
+ const params = [];
863
+ for (const [jsKey, column] of Object.entries(this.table.columns)) {
864
+ const value = resolveCreateValue(column, provided.get(jsKey));
865
+ sqlNames.push(quoteIdentifier(column.sqlName));
866
+ placeholders.push(nextPlaceholder());
867
+ params.push(serializeColumnValue(column, value));
868
+ }
869
+ const columns = buildSelectColumns(this.table, options.select);
870
+ const include = this.buildInclude(nextPlaceholder, options.include);
871
+ const returning = buildSelectList(columns, include);
872
+ return {
873
+ statement: `INSERT INTO ${quoteIdentifier(this.table.sqlName)} (${sqlNames.join(", ")}) VALUES (${placeholders.join(", ")}) RETURNING ${returning}`,
874
+ params: [...params, ...include.params],
875
+ includeDescriptors: include.descriptors
876
+ };
877
+ }
878
+ buildUpdate(options) {
879
+ validateFindUniqueWhere(this.table, options.where);
880
+ const nextPlaceholder = createNextPlaceholder(this.spec);
881
+ const { setClauses, params } = buildSetClauses({
882
+ nextPlaceholder,
883
+ table: this.table,
884
+ data: options.data
885
+ });
886
+ if (!setClauses.length) throw new Error("update requires at least one field in data");
887
+ const { where, include, returning } = this.buildWhereIncludeReturning(nextPlaceholder, {
888
+ where: options.where,
889
+ select: options.select,
890
+ include: options.include
891
+ });
892
+ let statement = `UPDATE ${quoteIdentifier(this.table.sqlName)} SET ${setClauses.join(", ")}`;
893
+ if (where.sql) statement = `${statement} WHERE ${where.sql}`;
894
+ statement = `${statement} RETURNING ${returning}`;
895
+ params.push(...where.params, ...include.params);
896
+ return {
897
+ statement,
898
+ params,
899
+ includeDescriptors: include.descriptors
900
+ };
901
+ }
902
+ buildDelete(options) {
903
+ validateFindUniqueWhere(this.table, options.where);
904
+ const nextPlaceholder = createNextPlaceholder(this.spec);
905
+ const { where, include, returning } = this.buildWhereIncludeReturning(nextPlaceholder, {
906
+ where: options.where,
907
+ select: options.select,
908
+ include: options.include
909
+ });
910
+ let statement = `DELETE FROM ${quoteIdentifier(this.table.sqlName)}`;
911
+ if (where.sql) statement = `${statement} WHERE ${where.sql}`;
912
+ statement = `${statement} RETURNING ${returning}`;
913
+ return {
914
+ statement,
915
+ params: [...where.params, ...include.params],
916
+ includeDescriptors: include.descriptors
917
+ };
918
+ }
919
+ buildCreateMany(options) {
920
+ if (!options.data.length) return {
921
+ statement: "",
922
+ params: [],
923
+ includeDescriptors: []
924
+ };
925
+ const nextPlaceholder = createNextPlaceholder(this.spec);
926
+ const columnEntries = Object.entries(this.table.columns);
927
+ const sqlNames = columnEntries.map(([, col]) => {
928
+ return quoteIdentifier(col.sqlName);
929
+ });
930
+ const params = [];
931
+ const rowPlaceholders = [];
932
+ for (const row of options.data) {
933
+ const rowRecord = row;
934
+ const placeholders = [];
935
+ for (const [jsKey, column] of columnEntries) {
936
+ const value = resolveCreateValue(column, rowRecord[jsKey]);
937
+ placeholders.push(nextPlaceholder());
938
+ params.push(serializeColumnValue(column, value));
939
+ }
940
+ rowPlaceholders.push(`(${placeholders.join(", ")})`);
941
+ }
942
+ const returning = buildSelectColumns(this.table, void 0);
943
+ return {
944
+ statement: `INSERT INTO ${quoteIdentifier(this.table.sqlName)} (${sqlNames.join(", ")}) VALUES ${rowPlaceholders.join(", ")} RETURNING ${returning}`,
945
+ params,
946
+ includeDescriptors: []
947
+ };
948
+ }
949
+ buildUpdateMany(options) {
950
+ const nextPlaceholder = createNextPlaceholder(this.spec);
951
+ const { setClauses, params } = buildSetClauses({
952
+ nextPlaceholder,
953
+ table: this.table,
954
+ data: options.data
955
+ });
956
+ if (!setClauses.length) throw new Error("updateMany requires at least one field in data");
957
+ const where = buildWhereClause({
958
+ nextPlaceholder,
959
+ table: this.table,
960
+ where: options.where,
961
+ relations: this.relations,
962
+ parentAlias: quoteIdentifier(this.table.sqlName)
963
+ });
964
+ let statement = `UPDATE ${quoteIdentifier(this.table.sqlName)} SET ${setClauses.join(", ")}`;
965
+ if (where.sql) statement = `${statement} WHERE ${where.sql}`;
966
+ params.push(...where.params);
967
+ const returning = buildSelectColumns(this.table, void 0);
968
+ statement = `${statement} RETURNING ${returning}`;
969
+ return {
970
+ statement,
971
+ params,
972
+ includeDescriptors: []
973
+ };
974
+ }
975
+ buildDeleteMany(options) {
976
+ const where = buildWhereClause({
977
+ nextPlaceholder: createNextPlaceholder(this.spec),
978
+ table: this.table,
979
+ where: options.where,
980
+ relations: this.relations,
981
+ parentAlias: quoteIdentifier(this.table.sqlName)
982
+ });
983
+ let statement = `DELETE FROM ${quoteIdentifier(this.table.sqlName)}`;
984
+ if (where.sql) statement = `${statement} WHERE ${where.sql}`;
985
+ const returning = buildSelectColumns(this.table, void 0);
986
+ statement = `${statement} RETURNING ${returning}`;
987
+ return {
988
+ statement,
989
+ params: where.params,
990
+ includeDescriptors: []
991
+ };
992
+ }
993
+ buildInclude(nextPlaceholder, include) {
994
+ return buildIncludeClause({
995
+ spec: this.spec,
996
+ nextPlaceholder,
997
+ table: this.table,
998
+ parentAlias: quoteIdentifier(this.table.sqlName),
999
+ relations: this.relations,
1000
+ tableRelationsMap: this.tableRelationsMap,
1001
+ include
1002
+ });
1003
+ }
1004
+ buildSelectIncludeWhere(nextPlaceholder, input) {
1005
+ const include = this.buildInclude(nextPlaceholder, input.include);
1006
+ return {
1007
+ include,
1008
+ where: buildWhereClause({
1009
+ nextPlaceholder,
1010
+ table: this.table,
1011
+ where: input.where,
1012
+ relations: this.relations,
1013
+ parentAlias: quoteIdentifier(this.table.sqlName)
1014
+ }),
1015
+ selectColumns: buildSelectList(buildSelectColumns(this.table, input.select), include)
1016
+ };
1017
+ }
1018
+ buildWhereIncludeReturning(nextPlaceholder, input) {
1019
+ const where = buildWhereClause({
1020
+ nextPlaceholder,
1021
+ table: this.table,
1022
+ where: input.where,
1023
+ relations: this.relations,
1024
+ parentAlias: quoteIdentifier(this.table.sqlName)
1025
+ });
1026
+ const columns = buildSelectColumns(this.table, input.select);
1027
+ const include = this.buildInclude(nextPlaceholder, input.include);
1028
+ return {
1029
+ where,
1030
+ include,
1031
+ returning: buildSelectList(columns, include)
1032
+ };
1033
+ }
1034
+ };
1035
+ //#endregion
1036
+ //#region src/lib/orm/dialect/rows.ts
1037
+ const coerceBooleanValue = (val) => {
1038
+ if (val === null) return val;
1039
+ if (val === void 0) return val;
1040
+ return Boolean(val);
1041
+ };
1042
+ const coerceRelationItems = (input) => {
1043
+ const { value, table, nested } = input;
1044
+ if (Array.isArray(value)) {
1045
+ for (const item of value) if (typeof item === "object" && item !== null) coerceRow({
1046
+ row: item,
1047
+ table,
1048
+ descriptors: nested
1049
+ });
1050
+ return;
1051
+ }
1052
+ if (typeof value === "object" && value !== null) coerceRow({
1053
+ row: value,
1054
+ table,
1055
+ descriptors: nested
1056
+ });
1057
+ };
1058
+ const getColumnKeysByType = (table) => {
1059
+ const boolKeys = /* @__PURE__ */ new Set();
1060
+ const jsonKeys = /* @__PURE__ */ new Set();
1061
+ for (const [key, col] of Object.entries(table.columns)) {
1062
+ if (col.type === "boolean") boolKeys.add(key);
1063
+ if (col.type === "json") jsonKeys.add(key);
1064
+ if (col.type === "jsonb") jsonKeys.add(key);
1065
+ }
1066
+ return {
1067
+ boolKeys,
1068
+ jsonKeys
1069
+ };
1070
+ };
1071
+ const coerceColumnValues = (row, table) => {
1072
+ const { boolKeys, jsonKeys } = getColumnKeysByType(table);
1073
+ for (const key of boolKeys) if (key in row) row[key] = coerceBooleanValue(row[key]);
1074
+ for (const key of jsonKeys) {
1075
+ if (!(key in row)) continue;
1076
+ const val = row[key];
1077
+ if (typeof val !== "string") continue;
1078
+ row[key] = JSON.parse(val);
1079
+ }
1080
+ };
1081
+ const coerceRelationValue = (input) => {
1082
+ const { row, descriptor } = input;
1083
+ const value = row[descriptor.name];
1084
+ if (value === null) {
1085
+ if (descriptor.type === "hasMany") row[descriptor.name] = [];
1086
+ return;
1087
+ }
1088
+ const nested = descriptor.nested ?? [];
1089
+ if (typeof value === "string") {
1090
+ const parsed = JSON.parse(value);
1091
+ coerceRelationItems({
1092
+ value: parsed,
1093
+ table: descriptor.table,
1094
+ nested
1095
+ });
1096
+ row[descriptor.name] = parsed;
1097
+ return;
1098
+ }
1099
+ coerceRelationItems({
1100
+ value,
1101
+ table: descriptor.table,
1102
+ nested
1103
+ });
1104
+ };
1105
+ const coerceRelationValues = (row, descriptors) => {
1106
+ for (const descriptor of descriptors) coerceRelationValue({
1107
+ row,
1108
+ descriptor
1109
+ });
1110
+ };
1111
+ const coerceRow = (input) => {
1112
+ const { row, table, descriptors } = input;
1113
+ coerceColumnValues(row, table);
1114
+ coerceRelationValues(row, descriptors);
1115
+ };
1116
+ const parseIncludeRows = (input) => {
1117
+ const { table, rows, descriptors } = input;
1118
+ for (const row of rows) coerceRow({
1119
+ row,
1120
+ table,
1121
+ descriptors
1122
+ });
1123
+ };
1124
+ const executeQuery = async (sql, table, query) => {
1125
+ const rows = [...await sql.unsafe(query.statement, query.params)];
1126
+ parseIncludeRows({
1127
+ table,
1128
+ rows,
1129
+ descriptors: query.includeDescriptors
1130
+ });
1131
+ return rows;
1132
+ };
1133
+ //#endregion
1134
+ //#region src/lib/orm/dialect/shared.ts
1135
+ const createDialect = (input) => {
1136
+ const { spec, table, relations, tableRelationsMap = /* @__PURE__ */ new Map() } = input;
1137
+ const builder = new DialectQueryBuilder({
1138
+ spec,
1139
+ table,
1140
+ relations,
1141
+ tableRelationsMap
1142
+ });
1143
+ const executeAndUnwrap = async (sql, query, operation) => {
1144
+ const [row] = await executeQuery(sql, table, query);
1145
+ if (!row) throw new Error(`Record not found after ${operation} on table ${table.sqlName}`);
1146
+ return row;
1147
+ };
1148
+ return {
1149
+ name: spec.name,
1150
+ findMany: async (sql, options) => {
1151
+ return await executeQuery(sql, table, builder.buildFindMany(options));
1152
+ },
1153
+ findFirst: async (sql, options) => {
1154
+ const [row] = await executeQuery(sql, table, builder.buildFindFirst(options));
1155
+ return row ?? null;
1156
+ },
1157
+ findUnique: async (sql, options) => {
1158
+ const [row] = await executeQuery(sql, table, builder.buildFindUnique(options));
1159
+ return row ?? null;
1160
+ },
1161
+ create: async (sql, options) => {
1162
+ return executeAndUnwrap(sql, builder.buildCreate(options), "insert");
1163
+ },
1164
+ createMany: async (sql, options) => {
1165
+ if (!options.data.length) return [];
1166
+ return await executeQuery(sql, table, builder.buildCreateMany(options));
1167
+ },
1168
+ update: async (sql, options) => {
1169
+ return executeAndUnwrap(sql, builder.buildUpdate(options), "update");
1170
+ },
1171
+ updateMany: async (sql, options) => {
1172
+ return await executeQuery(sql, table, builder.buildUpdateMany(options));
1173
+ },
1174
+ delete: async (sql, options) => {
1175
+ return executeAndUnwrap(sql, builder.buildDelete(options), "delete");
1176
+ },
1177
+ deleteMany: async (sql, options) => {
1178
+ return await executeQuery(sql, table, builder.buildDeleteMany(options));
1179
+ }
1180
+ };
1181
+ };
1182
+ //#endregion
1183
+ //#region src/lib/orm/dialect/postgres.ts
1184
+ const POSTGRES_SPEC = {
1185
+ name: "postgres",
1186
+ formatPlaceholder: (index) => `$${index}`,
1187
+ unlimitedOffsetKeyword: "LIMIT ALL OFFSET",
1188
+ jsonObjectFunctionName: "jsonb_build_object",
1189
+ jsonArrayAggregateFunctionName: "jsonb_agg",
1190
+ emptyJsonArrayLiteral: "'[]'::jsonb"
1191
+ };
1192
+ const createPostgresDialect = (input) => createDialect({
1193
+ spec: POSTGRES_SPEC,
1194
+ ...input
1195
+ });
1196
+ //#endregion
1197
+ //#region src/lib/orm/dialect/sqlite.ts
1198
+ const SQLITE_SPEC = {
1199
+ name: "sqlite",
1200
+ formatPlaceholder: () => "?",
1201
+ unlimitedOffsetKeyword: "LIMIT -1 OFFSET",
1202
+ jsonObjectFunctionName: "json_object",
1203
+ jsonArrayAggregateFunctionName: "json_group_array",
1204
+ emptyJsonArrayLiteral: "'[]'"
1205
+ };
1206
+ const createSqliteDialect = (input) => createDialect({
1207
+ spec: SQLITE_SPEC,
1208
+ ...input
1209
+ });
1210
+ //#endregion
1211
+ //#region src/lib/orm/dialect/index.ts
1212
+ const getDialect = (input) => {
1213
+ const { adapter, table, relations, tableRelationsMap = /* @__PURE__ */ new Map() } = input;
1214
+ switch (adapter) {
1215
+ case "sqlite": return createSqliteDialect({
1216
+ table,
1217
+ relations,
1218
+ tableRelationsMap
1219
+ });
1220
+ case "postgres": return createPostgresDialect({
1221
+ table,
1222
+ relations,
1223
+ tableRelationsMap
1224
+ });
1225
+ default: throw new Error(`Unsupported adapter: ${adapter}`);
1226
+ }
1227
+ };
1228
+ //#endregion
1229
+ //#region src/lib/orm/orm/index.ts
1230
+ const many = (table) => {
1231
+ return {
1232
+ _type: "hasMany",
1233
+ _table: table()
1234
+ };
1235
+ };
1236
+ const one = (foreignKey, table) => {
1237
+ return {
1238
+ _type: "hasOne",
1239
+ _table: table(),
1240
+ _foreignKey: foreignKey
1241
+ };
1242
+ };
1243
+ const shouldSkipHooks = (options) => {
1244
+ return options?.$skipHooks === true;
1245
+ };
1246
+ const withoutSkipHooks = (options) => {
1247
+ const { $skipHooks, ...queryOptions } = options;
1248
+ return queryOptions;
1249
+ };
1250
+ const toHookOptions = (options) => {
1251
+ if (!options) return;
1252
+ return withoutSkipHooks(options);
1253
+ };
1254
+ const pickGlobalHooks = (hooksConfig) => {
1255
+ const { tables, ...globalHooks } = hooksConfig;
1256
+ return globalHooks;
1257
+ };
1258
+ const runHook = async (hook, ctx) => {
1259
+ await hook?.(ctx);
1260
+ };
1261
+ const runReadHooks = async (globalHook, tableHook, ctx) => {
1262
+ await runHook(globalHook, ctx);
1263
+ await runHook(tableHook, ctx);
1264
+ };
1265
+ const withReadHooks = async (input) => {
1266
+ const beforeCtx = {
1267
+ tableName: input.tableName,
1268
+ table: input.table,
1269
+ options: input.hookOptions
1270
+ };
1271
+ if (!input.skipHooks) await runReadHooks(input.beforeGlobal, input.beforeTable, beforeCtx);
1272
+ const result = await input.query();
1273
+ if (!input.skipHooks) await runReadHooks(input.afterGlobal, input.afterTable, {
1274
+ ...beforeCtx,
1275
+ result
1276
+ });
1277
+ return result;
1278
+ };
1279
+ const createTableClient = (input) => {
1280
+ const { sql, tableName, table, adapter, relations, tableRelationsMap, globalHooks, tableHooks } = input;
1281
+ const dialect = getDialect({
1282
+ adapter,
1283
+ table,
1284
+ relations,
1285
+ tableRelationsMap
1286
+ });
1287
+ return {
1288
+ findMany: async (options) => {
1289
+ const skipHooks = shouldSkipHooks(options);
1290
+ const hookOptions = toHookOptions(options);
1291
+ return withReadHooks({
1292
+ skipHooks,
1293
+ tableName,
1294
+ table,
1295
+ hookOptions,
1296
+ beforeGlobal: globalHooks?.beforeFindMany,
1297
+ beforeTable: tableHooks?.beforeFindMany,
1298
+ afterGlobal: globalHooks?.afterFindMany,
1299
+ afterTable: tableHooks?.afterFindMany,
1300
+ query: () => dialect.findMany(sql, hookOptions)
1301
+ });
1302
+ },
1303
+ findFirst: async (options) => {
1304
+ const skipHooks = shouldSkipHooks(options);
1305
+ const hookOptions = toHookOptions(options);
1306
+ return withReadHooks({
1307
+ skipHooks,
1308
+ tableName,
1309
+ table,
1310
+ hookOptions,
1311
+ beforeGlobal: globalHooks?.beforeFindFirst,
1312
+ beforeTable: tableHooks?.beforeFindFirst,
1313
+ afterGlobal: globalHooks?.afterFindFirst,
1314
+ afterTable: tableHooks?.afterFindFirst,
1315
+ query: () => dialect.findFirst(sql, hookOptions)
1316
+ });
1317
+ },
1318
+ findUnique: async (options) => {
1319
+ const skipHooks = shouldSkipHooks(options);
1320
+ const hookOptions = withoutSkipHooks(options);
1321
+ return withReadHooks({
1322
+ skipHooks,
1323
+ tableName,
1324
+ table,
1325
+ hookOptions,
1326
+ beforeGlobal: globalHooks?.beforeFindUnique,
1327
+ beforeTable: tableHooks?.beforeFindUnique,
1328
+ afterGlobal: globalHooks?.afterFindUnique,
1329
+ afterTable: tableHooks?.afterFindUnique,
1330
+ query: () => dialect.findUnique(sql, hookOptions)
1331
+ });
1332
+ },
1333
+ create: async (options) => {
1334
+ const skipHooks = shouldSkipHooks(options);
1335
+ const hookOptions = withoutSkipHooks(options);
1336
+ let queryOptions = hookOptions;
1337
+ if (!skipHooks) {
1338
+ const createHookResult = await globalHooks?.beforeCreate?.({
1339
+ tableName,
1340
+ table,
1341
+ options: hookOptions
1342
+ });
1343
+ queryOptions = {
1344
+ ...hookOptions,
1345
+ ...createHookResult
1346
+ };
1347
+ const tableCreateHookResult = await tableHooks?.beforeCreate?.({
1348
+ tableName,
1349
+ table,
1350
+ options: queryOptions
1351
+ });
1352
+ queryOptions = {
1353
+ ...queryOptions,
1354
+ ...tableCreateHookResult
1355
+ };
1356
+ }
1357
+ const result = await dialect.create(sql, queryOptions);
1358
+ if (!skipHooks) await runReadHooks(globalHooks?.afterCreate, tableHooks?.afterCreate, {
1359
+ tableName,
1360
+ table,
1361
+ options: queryOptions,
1362
+ result
1363
+ });
1364
+ return result;
1365
+ },
1366
+ createMany: async (options) => {
1367
+ const skipHooks = shouldSkipHooks(options);
1368
+ const hookOptions = withoutSkipHooks(options);
1369
+ let queryOptions = hookOptions;
1370
+ if (!skipHooks) {
1371
+ const createManyHookResult = await globalHooks?.beforeCreateMany?.({
1372
+ tableName,
1373
+ table,
1374
+ options: hookOptions
1375
+ });
1376
+ queryOptions = {
1377
+ ...hookOptions,
1378
+ ...createManyHookResult
1379
+ };
1380
+ const tableCreateManyHookResult = await tableHooks?.beforeCreateMany?.({
1381
+ tableName,
1382
+ table,
1383
+ options: queryOptions
1384
+ });
1385
+ queryOptions = {
1386
+ ...queryOptions,
1387
+ ...tableCreateManyHookResult
1388
+ };
1389
+ }
1390
+ const result = await dialect.createMany(sql, queryOptions);
1391
+ if (!skipHooks) {
1392
+ const hookCtx = {
1393
+ tableName,
1394
+ table,
1395
+ options: queryOptions,
1396
+ result
1397
+ };
1398
+ await runHook(globalHooks?.afterCreateMany, hookCtx);
1399
+ await runHook(tableHooks?.afterCreateMany, hookCtx);
1400
+ }
1401
+ return result;
1402
+ },
1403
+ update: async (options) => {
1404
+ const skipHooks = shouldSkipHooks(options);
1405
+ const hookOptions = withoutSkipHooks(options);
1406
+ let queryOptions = hookOptions;
1407
+ if (!skipHooks) {
1408
+ const updateHookResult = await globalHooks?.beforeUpdate?.({
1409
+ tableName,
1410
+ table,
1411
+ options: hookOptions
1412
+ });
1413
+ queryOptions = {
1414
+ ...hookOptions,
1415
+ ...updateHookResult
1416
+ };
1417
+ const tableUpdateHookResult = await tableHooks?.beforeUpdate?.({
1418
+ tableName,
1419
+ table,
1420
+ options: queryOptions
1421
+ });
1422
+ queryOptions = {
1423
+ ...queryOptions,
1424
+ ...tableUpdateHookResult
1425
+ };
1426
+ }
1427
+ const result = await dialect.update(sql, queryOptions);
1428
+ if (!skipHooks) await runReadHooks(globalHooks?.afterUpdate, tableHooks?.afterUpdate, {
1429
+ tableName,
1430
+ table,
1431
+ options: queryOptions,
1432
+ result
1433
+ });
1434
+ return result;
1435
+ },
1436
+ updateMany: async (options) => {
1437
+ const skipHooks = shouldSkipHooks(options);
1438
+ const hookOptions = withoutSkipHooks(options);
1439
+ let queryOptions = hookOptions;
1440
+ if (!skipHooks) {
1441
+ const updateManyHookResult = await globalHooks?.beforeUpdateMany?.({
1442
+ tableName,
1443
+ table,
1444
+ options: hookOptions
1445
+ });
1446
+ queryOptions = {
1447
+ ...hookOptions,
1448
+ ...updateManyHookResult
1449
+ };
1450
+ const tableUpdateManyHookResult = await tableHooks?.beforeUpdateMany?.({
1451
+ tableName,
1452
+ table,
1453
+ options: queryOptions
1454
+ });
1455
+ queryOptions = {
1456
+ ...queryOptions,
1457
+ ...tableUpdateManyHookResult
1458
+ };
1459
+ }
1460
+ const result = await dialect.updateMany(sql, queryOptions);
1461
+ if (!skipHooks) {
1462
+ const hookCtx = {
1463
+ tableName,
1464
+ table,
1465
+ options: queryOptions,
1466
+ result
1467
+ };
1468
+ await runHook(globalHooks?.afterUpdateMany, hookCtx);
1469
+ await runHook(tableHooks?.afterUpdateMany, hookCtx);
1470
+ }
1471
+ return result;
1472
+ },
1473
+ delete: async (options) => {
1474
+ const skipHooks = shouldSkipHooks(options);
1475
+ const hookOptions = withoutSkipHooks(options);
1476
+ let queryOptions = hookOptions;
1477
+ if (!skipHooks) {
1478
+ const deleteHookResult = await globalHooks?.beforeDelete?.({
1479
+ tableName,
1480
+ table,
1481
+ options: hookOptions
1482
+ });
1483
+ queryOptions = {
1484
+ ...hookOptions,
1485
+ ...deleteHookResult
1486
+ };
1487
+ const tableDeleteHookResult = await tableHooks?.beforeDelete?.({
1488
+ tableName,
1489
+ table,
1490
+ options: queryOptions
1491
+ });
1492
+ queryOptions = {
1493
+ ...queryOptions,
1494
+ ...tableDeleteHookResult
1495
+ };
1496
+ }
1497
+ const result = await dialect.delete(sql, queryOptions);
1498
+ if (!skipHooks) await runReadHooks(globalHooks?.afterDelete, tableHooks?.afterDelete, {
1499
+ tableName,
1500
+ table,
1501
+ options: queryOptions,
1502
+ result
1503
+ });
1504
+ return result;
1505
+ },
1506
+ deleteMany: async (options) => {
1507
+ const skipHooks = shouldSkipHooks(options);
1508
+ const hookOptions = withoutSkipHooks(options);
1509
+ let queryOptions = hookOptions;
1510
+ if (!skipHooks) {
1511
+ const deleteManyHookResult = await globalHooks?.beforeDeleteMany?.({
1512
+ tableName,
1513
+ table,
1514
+ options: hookOptions
1515
+ });
1516
+ queryOptions = {
1517
+ ...hookOptions,
1518
+ ...deleteManyHookResult
1519
+ };
1520
+ const tableDeleteManyHookResult = await tableHooks?.beforeDeleteMany?.({
1521
+ tableName,
1522
+ table,
1523
+ options: queryOptions
1524
+ });
1525
+ queryOptions = {
1526
+ ...queryOptions,
1527
+ ...tableDeleteManyHookResult
1528
+ };
1529
+ }
1530
+ const result = await dialect.deleteMany(sql, queryOptions);
1531
+ if (!skipHooks) {
1532
+ const hookCtx = {
1533
+ tableName,
1534
+ table,
1535
+ options: queryOptions,
1536
+ result
1537
+ };
1538
+ await runHook(globalHooks?.afterDeleteMany, hookCtx);
1539
+ await runHook(tableHooks?.afterDeleteMany, hookCtx);
1540
+ }
1541
+ return result;
1542
+ }
1543
+ };
1544
+ };
1545
+ const toObjectEntries = (object) => {
1546
+ const result = [];
1547
+ for (const key in object) {
1548
+ if (!Object.hasOwn(object, key)) continue;
1549
+ result.push([key, object[key]]);
1550
+ }
1551
+ return result;
1552
+ };
1553
+ const getTableRelations = (relations, tableName) => {
1554
+ if (!relations) return Object.create(null);
1555
+ if (!Object.hasOwn(relations, tableName)) return Object.create(null);
1556
+ const tableRelations = relations[tableName];
1557
+ if (!tableRelations) return Object.create(null);
1558
+ return tableRelations;
1559
+ };
1560
+ const buildTableClients = (input) => {
1561
+ const { tables, relations, sql, adapter, tableRelationsMap, hooks } = input;
1562
+ const clients = Object.create(null);
1563
+ for (const entry of toObjectEntries(tables)) {
1564
+ const setTableClient = (tableName, table) => {
1565
+ const tableRelations = getTableRelations(relations, tableName);
1566
+ tableRelationsMap.set(table, tableRelations);
1567
+ clients[tableName] = createTableClient({
1568
+ sql,
1569
+ tableName,
1570
+ table,
1571
+ adapter,
1572
+ relations: tableRelations,
1573
+ tableRelationsMap,
1574
+ globalHooks: hooks ? pickGlobalHooks(hooks) : void 0,
1575
+ tableHooks: hooks?.tables?.[tableName]
1576
+ });
1577
+ };
1578
+ setTableClient(entry[0], entry[1]);
1579
+ }
1580
+ return clients;
1581
+ };
1582
+ const buildOrmClient = (input) => {
1583
+ const { tableClients, sql, transaction } = input;
1584
+ return {
1585
+ ...tableClients,
1586
+ $raw: sql,
1587
+ $transaction: transaction
1588
+ };
1589
+ };
1590
+ const buildTransactionClient = (tableClients, sql) => ({
1591
+ ...tableClients,
1592
+ $raw: sql
1593
+ });
1594
+ const createOrm = (options) => {
1595
+ const sql = new Bun.SQL(options.url, { adapter: options.adapter });
1596
+ const tableRelationsMap = /* @__PURE__ */ new Map();
1597
+ return buildOrmClient({
1598
+ tableClients: buildTableClients({
1599
+ tables: options.tables,
1600
+ relations: options.relations,
1601
+ sql,
1602
+ adapter: options.adapter,
1603
+ tableRelationsMap,
1604
+ hooks: options.hooks
1605
+ }),
1606
+ sql,
1607
+ transaction: async (callback) => {
1608
+ return await sql.begin(async (txSql) => {
1609
+ return await callback(buildTransactionClient(buildTableClients({
1610
+ tables: options.tables,
1611
+ relations: options.relations,
1612
+ sql: txSql,
1613
+ adapter: options.adapter,
1614
+ tableRelationsMap,
1615
+ hooks: options.hooks
1616
+ }), txSql));
1617
+ });
1618
+ }
1619
+ });
1620
+ };
1621
+ //#endregion
1622
+ //#region src/lib/orm/table/index.ts
1623
+ const defineTable = (sqlName, columns) => {
1624
+ return {
1625
+ sqlName,
1626
+ columns
1627
+ };
1628
+ };
1629
+ //#endregion
1630
+ export { boolean, createOrm, date, defineTable, enumType, json, jsonb, many, number, one, string, uuid };