@vertz/db 0.2.0 → 0.2.3
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 +412 -694
- package/dist/d1/index.d.ts +241 -0
- package/dist/d1/index.js +8 -0
- package/dist/diagnostic/index.js +1 -1
- package/dist/index.d.ts +932 -626
- package/dist/index.js +1759 -533
- package/dist/internals.d.ts +96 -31
- package/dist/internals.js +8 -7
- package/dist/plugin/index.d.ts +1 -1
- package/dist/plugin/index.js +7 -3
- package/dist/postgres/index.d.ts +77 -0
- package/dist/postgres/index.js +7 -0
- package/dist/shared/{chunk-3f2grpak.js → chunk-0e1vy9qd.js} +147 -52
- package/dist/shared/chunk-2gd1fqcw.js +7 -0
- package/dist/shared/{chunk-xp022dyp.js → chunk-agyds4jw.js} +25 -19
- package/dist/shared/chunk-dvwe5jsq.js +7 -0
- package/dist/shared/chunk-j4kwq1gh.js +5 -0
- package/dist/shared/{chunk-wj026daz.js → chunk-k04v1jjx.js} +2 -2
- package/dist/shared/chunk-kb4tnn2k.js +26 -0
- package/dist/shared/chunk-pnk6yzjv.js +48 -0
- package/dist/shared/chunk-rqe0prft.js +100 -0
- package/dist/shared/chunk-sfmyxz6r.js +306 -0
- package/dist/shared/chunk-ssga2xea.js +9 -0
- package/dist/shared/{chunk-hrfdj0rr.js → chunk-v2qm94qp.js} +12 -2
- package/dist/sql/index.d.ts +61 -61
- package/dist/sql/index.js +2 -2
- package/dist/sqlite/index.d.ts +221 -0
- package/dist/sqlite/index.js +845 -0
- package/package.json +32 -5
|
@@ -1,7 +1,89 @@
|
|
|
1
1
|
import {
|
|
2
2
|
camelToSnake
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-v2qm94qp.js";
|
|
4
4
|
|
|
5
|
+
// src/dialect/postgres.ts
|
|
6
|
+
class PostgresDialect {
|
|
7
|
+
name = "postgres";
|
|
8
|
+
supportsReturning = true;
|
|
9
|
+
supportsArrayOps = true;
|
|
10
|
+
supportsJsonbPath = true;
|
|
11
|
+
param(index) {
|
|
12
|
+
return `$${index}`;
|
|
13
|
+
}
|
|
14
|
+
now() {
|
|
15
|
+
return "NOW()";
|
|
16
|
+
}
|
|
17
|
+
mapColumnType(sqlType, meta) {
|
|
18
|
+
switch (sqlType) {
|
|
19
|
+
case "uuid":
|
|
20
|
+
return "UUID";
|
|
21
|
+
case "text":
|
|
22
|
+
return "TEXT";
|
|
23
|
+
case "integer":
|
|
24
|
+
return "INTEGER";
|
|
25
|
+
case "serial":
|
|
26
|
+
return "SERIAL";
|
|
27
|
+
case "boolean":
|
|
28
|
+
return "BOOLEAN";
|
|
29
|
+
case "timestamp":
|
|
30
|
+
return "TIMESTAMPTZ";
|
|
31
|
+
case "float":
|
|
32
|
+
return "DOUBLE PRECISION";
|
|
33
|
+
case "json":
|
|
34
|
+
return "JSONB";
|
|
35
|
+
case "decimal":
|
|
36
|
+
return meta?.precision ? `NUMERIC(${meta.precision},${meta.scale ?? 0})` : "NUMERIC";
|
|
37
|
+
case "varchar":
|
|
38
|
+
return meta?.length ? `VARCHAR(${meta.length})` : "VARCHAR";
|
|
39
|
+
case "enum":
|
|
40
|
+
return meta?.enumName ?? "TEXT";
|
|
41
|
+
default:
|
|
42
|
+
return "TEXT";
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
var defaultPostgresDialect = new PostgresDialect;
|
|
47
|
+
|
|
48
|
+
// src/dialect/sqlite.ts
|
|
49
|
+
class SqliteDialect {
|
|
50
|
+
name = "sqlite";
|
|
51
|
+
supportsReturning = true;
|
|
52
|
+
supportsArrayOps = false;
|
|
53
|
+
supportsJsonbPath = false;
|
|
54
|
+
param(_index) {
|
|
55
|
+
return "?";
|
|
56
|
+
}
|
|
57
|
+
now() {
|
|
58
|
+
return "datetime('now')";
|
|
59
|
+
}
|
|
60
|
+
mapColumnType(sqlType, _meta) {
|
|
61
|
+
switch (sqlType) {
|
|
62
|
+
case "uuid":
|
|
63
|
+
return "TEXT";
|
|
64
|
+
case "boolean":
|
|
65
|
+
return "INTEGER";
|
|
66
|
+
case "timestamp":
|
|
67
|
+
return "TEXT";
|
|
68
|
+
case "json":
|
|
69
|
+
case "jsonb":
|
|
70
|
+
return "TEXT";
|
|
71
|
+
case "decimal":
|
|
72
|
+
return "REAL";
|
|
73
|
+
case "text":
|
|
74
|
+
return "TEXT";
|
|
75
|
+
case "integer":
|
|
76
|
+
return "INTEGER";
|
|
77
|
+
case "bigint":
|
|
78
|
+
return "INTEGER";
|
|
79
|
+
case "serial":
|
|
80
|
+
return "INTEGER";
|
|
81
|
+
default:
|
|
82
|
+
return "TEXT";
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
var defaultSqliteDialect = new SqliteDialect;
|
|
5
87
|
// src/sql/where.ts
|
|
6
88
|
var OPERATOR_KEYS = new Set([
|
|
7
89
|
"eq",
|
|
@@ -33,11 +115,14 @@ function escapeSingleQuotes(str) {
|
|
|
33
115
|
function escapeLikeValue(str) {
|
|
34
116
|
return str.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
|
|
35
117
|
}
|
|
36
|
-
function resolveColumnRef(key) {
|
|
118
|
+
function resolveColumnRef(key, overrides, dialect) {
|
|
37
119
|
if (key.includes("->")) {
|
|
120
|
+
if (dialect && !dialect.supportsJsonbPath) {
|
|
121
|
+
throw new Error("JSONB path operators (->>, ->) are not supported on SQLite. " + "Use json_extract() via raw SQL or switch to Postgres.");
|
|
122
|
+
}
|
|
38
123
|
const parts = key.split("->");
|
|
39
124
|
const baseCol = parts[0] ?? key;
|
|
40
|
-
const column = `"${camelToSnake(baseCol)}"`;
|
|
125
|
+
const column = `"${camelToSnake(baseCol, overrides)}"`;
|
|
41
126
|
const jsonPath = parts.slice(1);
|
|
42
127
|
if (jsonPath.length === 1) {
|
|
43
128
|
return `${column}->>'${escapeSingleQuotes(jsonPath[0] ?? "")}'`;
|
|
@@ -47,54 +132,54 @@ function resolveColumnRef(key) {
|
|
|
47
132
|
const final = `->>'${escapeSingleQuotes(lastKey)}'`;
|
|
48
133
|
return `${column}${intermediate}${final}`;
|
|
49
134
|
}
|
|
50
|
-
return `"${camelToSnake(key)}"`;
|
|
135
|
+
return `"${camelToSnake(key, overrides)}"`;
|
|
51
136
|
}
|
|
52
|
-
function buildOperatorCondition(columnRef, operators, paramIndex) {
|
|
137
|
+
function buildOperatorCondition(columnRef, operators, paramIndex, dialect) {
|
|
53
138
|
const clauses = [];
|
|
54
139
|
const params = [];
|
|
55
140
|
let idx = paramIndex;
|
|
56
141
|
if (operators.eq !== undefined) {
|
|
57
|
-
clauses.push(`${columnRef} =
|
|
142
|
+
clauses.push(`${columnRef} = ${dialect.param(idx + 1)}`);
|
|
58
143
|
params.push(operators.eq);
|
|
59
144
|
idx++;
|
|
60
145
|
}
|
|
61
146
|
if (operators.ne !== undefined) {
|
|
62
|
-
clauses.push(`${columnRef} !=
|
|
147
|
+
clauses.push(`${columnRef} != ${dialect.param(idx + 1)}`);
|
|
63
148
|
params.push(operators.ne);
|
|
64
149
|
idx++;
|
|
65
150
|
}
|
|
66
151
|
if (operators.gt !== undefined) {
|
|
67
|
-
clauses.push(`${columnRef} >
|
|
152
|
+
clauses.push(`${columnRef} > ${dialect.param(idx + 1)}`);
|
|
68
153
|
params.push(operators.gt);
|
|
69
154
|
idx++;
|
|
70
155
|
}
|
|
71
156
|
if (operators.gte !== undefined) {
|
|
72
|
-
clauses.push(`${columnRef} >=
|
|
157
|
+
clauses.push(`${columnRef} >= ${dialect.param(idx + 1)}`);
|
|
73
158
|
params.push(operators.gte);
|
|
74
159
|
idx++;
|
|
75
160
|
}
|
|
76
161
|
if (operators.lt !== undefined) {
|
|
77
|
-
clauses.push(`${columnRef} <
|
|
162
|
+
clauses.push(`${columnRef} < ${dialect.param(idx + 1)}`);
|
|
78
163
|
params.push(operators.lt);
|
|
79
164
|
idx++;
|
|
80
165
|
}
|
|
81
166
|
if (operators.lte !== undefined) {
|
|
82
|
-
clauses.push(`${columnRef} <=
|
|
167
|
+
clauses.push(`${columnRef} <= ${dialect.param(idx + 1)}`);
|
|
83
168
|
params.push(operators.lte);
|
|
84
169
|
idx++;
|
|
85
170
|
}
|
|
86
171
|
if (operators.contains !== undefined) {
|
|
87
|
-
clauses.push(`${columnRef} LIKE
|
|
172
|
+
clauses.push(`${columnRef} LIKE ${dialect.param(idx + 1)}`);
|
|
88
173
|
params.push(`%${escapeLikeValue(operators.contains)}%`);
|
|
89
174
|
idx++;
|
|
90
175
|
}
|
|
91
176
|
if (operators.startsWith !== undefined) {
|
|
92
|
-
clauses.push(`${columnRef} LIKE
|
|
177
|
+
clauses.push(`${columnRef} LIKE ${dialect.param(idx + 1)}`);
|
|
93
178
|
params.push(`${escapeLikeValue(operators.startsWith)}%`);
|
|
94
179
|
idx++;
|
|
95
180
|
}
|
|
96
181
|
if (operators.endsWith !== undefined) {
|
|
97
|
-
clauses.push(`${columnRef} LIKE
|
|
182
|
+
clauses.push(`${columnRef} LIKE ${dialect.param(idx + 1)}`);
|
|
98
183
|
params.push(`%${escapeLikeValue(operators.endsWith)}`);
|
|
99
184
|
idx++;
|
|
100
185
|
}
|
|
@@ -102,7 +187,7 @@ function buildOperatorCondition(columnRef, operators, paramIndex) {
|
|
|
102
187
|
if (operators.in.length === 0) {
|
|
103
188
|
clauses.push("FALSE");
|
|
104
189
|
} else {
|
|
105
|
-
const placeholders = operators.in.map((_, i) =>
|
|
190
|
+
const placeholders = operators.in.map((_, i) => dialect.param(idx + 1 + i)).join(", ");
|
|
106
191
|
clauses.push(`${columnRef} IN (${placeholders})`);
|
|
107
192
|
params.push(...operators.in);
|
|
108
193
|
idx += operators.in.length;
|
|
@@ -112,7 +197,7 @@ function buildOperatorCondition(columnRef, operators, paramIndex) {
|
|
|
112
197
|
if (operators.notIn.length === 0) {
|
|
113
198
|
clauses.push("TRUE");
|
|
114
199
|
} else {
|
|
115
|
-
const placeholders = operators.notIn.map((_, i) =>
|
|
200
|
+
const placeholders = operators.notIn.map((_, i) => dialect.param(idx + 1 + i)).join(", ");
|
|
116
201
|
clauses.push(`${columnRef} NOT IN (${placeholders})`);
|
|
117
202
|
params.push(...operators.notIn);
|
|
118
203
|
idx += operators.notIn.length;
|
|
@@ -122,23 +207,32 @@ function buildOperatorCondition(columnRef, operators, paramIndex) {
|
|
|
122
207
|
clauses.push(`${columnRef} ${operators.isNull ? "IS NULL" : "IS NOT NULL"}`);
|
|
123
208
|
}
|
|
124
209
|
if (operators.arrayContains !== undefined) {
|
|
125
|
-
|
|
210
|
+
if (!dialect.supportsArrayOps) {
|
|
211
|
+
throw new Error("Array operators (arrayContains, arrayContainedBy, arrayOverlaps) are not supported on SQLite. " + "Use a different filter strategy or switch to Postgres.");
|
|
212
|
+
}
|
|
213
|
+
clauses.push(`${columnRef} @> ${dialect.param(idx + 1)}`);
|
|
126
214
|
params.push(operators.arrayContains);
|
|
127
215
|
idx++;
|
|
128
216
|
}
|
|
129
217
|
if (operators.arrayContainedBy !== undefined) {
|
|
130
|
-
|
|
218
|
+
if (!dialect.supportsArrayOps) {
|
|
219
|
+
throw new Error("Array operators (arrayContains, arrayContainedBy, arrayOverlaps) are not supported on SQLite. " + "Use a different filter strategy or switch to Postgres.");
|
|
220
|
+
}
|
|
221
|
+
clauses.push(`${columnRef} <@ ${dialect.param(idx + 1)}`);
|
|
131
222
|
params.push(operators.arrayContainedBy);
|
|
132
223
|
idx++;
|
|
133
224
|
}
|
|
134
225
|
if (operators.arrayOverlaps !== undefined) {
|
|
135
|
-
|
|
226
|
+
if (!dialect.supportsArrayOps) {
|
|
227
|
+
throw new Error("Array operators (arrayContains, arrayContainedBy, arrayOverlaps) are not supported on SQLite. " + "Use a different filter strategy or switch to Postgres.");
|
|
228
|
+
}
|
|
229
|
+
clauses.push(`${columnRef} && ${dialect.param(idx + 1)}`);
|
|
136
230
|
params.push(operators.arrayOverlaps);
|
|
137
231
|
idx++;
|
|
138
232
|
}
|
|
139
233
|
return { clauses, params, nextIndex: idx };
|
|
140
234
|
}
|
|
141
|
-
function buildFilterClauses(filter, paramOffset) {
|
|
235
|
+
function buildFilterClauses(filter, paramOffset, overrides, dialect) {
|
|
142
236
|
const clauses = [];
|
|
143
237
|
const allParams = [];
|
|
144
238
|
let idx = paramOffset;
|
|
@@ -146,14 +240,14 @@ function buildFilterClauses(filter, paramOffset) {
|
|
|
146
240
|
if (key === "OR" || key === "AND" || key === "NOT") {
|
|
147
241
|
continue;
|
|
148
242
|
}
|
|
149
|
-
const columnRef = resolveColumnRef(key);
|
|
243
|
+
const columnRef = resolveColumnRef(key, overrides, dialect);
|
|
150
244
|
if (isOperatorObject(value)) {
|
|
151
|
-
const result = buildOperatorCondition(columnRef, value, idx);
|
|
245
|
+
const result = buildOperatorCondition(columnRef, value, idx, dialect);
|
|
152
246
|
clauses.push(...result.clauses);
|
|
153
247
|
allParams.push(...result.params);
|
|
154
248
|
idx = result.nextIndex;
|
|
155
249
|
} else {
|
|
156
|
-
clauses.push(`${columnRef} =
|
|
250
|
+
clauses.push(`${columnRef} = ${dialect.param(idx + 1)}`);
|
|
157
251
|
allParams.push(value);
|
|
158
252
|
idx++;
|
|
159
253
|
}
|
|
@@ -164,7 +258,7 @@ function buildFilterClauses(filter, paramOffset) {
|
|
|
164
258
|
} else {
|
|
165
259
|
const orClauses = [];
|
|
166
260
|
for (const subFilter of filter.OR) {
|
|
167
|
-
const sub = buildFilterClauses(subFilter, idx);
|
|
261
|
+
const sub = buildFilterClauses(subFilter, idx, overrides, dialect);
|
|
168
262
|
const joined = sub.clauses.join(" AND ");
|
|
169
263
|
orClauses.push(sub.clauses.length > 1 ? `(${joined})` : joined);
|
|
170
264
|
allParams.push(...sub.params);
|
|
@@ -179,7 +273,7 @@ function buildFilterClauses(filter, paramOffset) {
|
|
|
179
273
|
} else {
|
|
180
274
|
const andClauses = [];
|
|
181
275
|
for (const subFilter of filter.AND) {
|
|
182
|
-
const sub = buildFilterClauses(subFilter, idx);
|
|
276
|
+
const sub = buildFilterClauses(subFilter, idx, overrides, dialect);
|
|
183
277
|
const joined = sub.clauses.join(" AND ");
|
|
184
278
|
andClauses.push(sub.clauses.length > 1 ? `(${joined})` : joined);
|
|
185
279
|
allParams.push(...sub.params);
|
|
@@ -189,18 +283,18 @@ function buildFilterClauses(filter, paramOffset) {
|
|
|
189
283
|
}
|
|
190
284
|
}
|
|
191
285
|
if (filter.NOT !== undefined) {
|
|
192
|
-
const sub = buildFilterClauses(filter.NOT, idx);
|
|
286
|
+
const sub = buildFilterClauses(filter.NOT, idx, overrides, dialect);
|
|
193
287
|
clauses.push(`NOT (${sub.clauses.join(" AND ")})`);
|
|
194
288
|
allParams.push(...sub.params);
|
|
195
289
|
idx = sub.nextIndex;
|
|
196
290
|
}
|
|
197
291
|
return { clauses, params: allParams, nextIndex: idx };
|
|
198
292
|
}
|
|
199
|
-
function buildWhere(filter, paramOffset = 0) {
|
|
293
|
+
function buildWhere(filter, paramOffset = 0, overrides, dialect = defaultPostgresDialect) {
|
|
200
294
|
if (!filter || Object.keys(filter).length === 0) {
|
|
201
295
|
return { sql: "", params: [] };
|
|
202
296
|
}
|
|
203
|
-
const { clauses, params } = buildFilterClauses(filter, paramOffset);
|
|
297
|
+
const { clauses, params } = buildFilterClauses(filter, paramOffset, overrides, dialect);
|
|
204
298
|
return {
|
|
205
299
|
sql: clauses.join(" AND "),
|
|
206
300
|
params
|
|
@@ -215,11 +309,11 @@ function buildReturningColumnRef(name) {
|
|
|
215
309
|
}
|
|
216
310
|
return `"${snakeName}" AS "${name}"`;
|
|
217
311
|
}
|
|
218
|
-
function buildDelete(options) {
|
|
312
|
+
function buildDelete(options, dialect = options.dialect ?? defaultPostgresDialect) {
|
|
219
313
|
const allParams = [];
|
|
220
314
|
let sql = `DELETE FROM "${options.table}"`;
|
|
221
315
|
if (options.where) {
|
|
222
|
-
const whereResult = buildWhere(options.where);
|
|
316
|
+
const whereResult = buildWhere(options.where, 0, undefined, dialect);
|
|
223
317
|
if (whereResult.sql.length > 0) {
|
|
224
318
|
sql += ` WHERE ${whereResult.sql}`;
|
|
225
319
|
allParams.push(...whereResult.params);
|
|
@@ -244,7 +338,7 @@ function buildReturningColumnRef2(name) {
|
|
|
244
338
|
}
|
|
245
339
|
return `"${snakeName}" AS "${name}"`;
|
|
246
340
|
}
|
|
247
|
-
function buildInsert(options) {
|
|
341
|
+
function buildInsert(options, dialect = options.dialect ?? defaultPostgresDialect) {
|
|
248
342
|
const rows = Array.isArray(options.data) ? options.data : [options.data];
|
|
249
343
|
const firstRow = rows[0];
|
|
250
344
|
if (!firstRow) {
|
|
@@ -260,10 +354,10 @@ function buildInsert(options) {
|
|
|
260
354
|
for (const key of keys) {
|
|
261
355
|
const value = row[key];
|
|
262
356
|
if (nowSet.has(key) && value === "now") {
|
|
263
|
-
placeholders.push(
|
|
357
|
+
placeholders.push(dialect.now());
|
|
264
358
|
} else {
|
|
265
359
|
allParams.push(value);
|
|
266
|
-
placeholders.push(
|
|
360
|
+
placeholders.push(dialect.param(allParams.length));
|
|
267
361
|
}
|
|
268
362
|
}
|
|
269
363
|
valuesClauses.push(`(${placeholders.join(", ")})`);
|
|
@@ -279,7 +373,7 @@ function buildInsert(options) {
|
|
|
279
373
|
const setClauses = options.onConflict.updateColumns.map((c) => {
|
|
280
374
|
const snakeCol = camelToSnake(c);
|
|
281
375
|
allParams.push(updateVals[c]);
|
|
282
|
-
return `"${snakeCol}" =
|
|
376
|
+
return `"${snakeCol}" = ${dialect.param(allParams.length)}`;
|
|
283
377
|
}).join(", ");
|
|
284
378
|
sql += ` ON CONFLICT (${conflictCols}) DO UPDATE SET ${setClauses}`;
|
|
285
379
|
} else {
|
|
@@ -303,19 +397,20 @@ function buildInsert(options) {
|
|
|
303
397
|
}
|
|
304
398
|
|
|
305
399
|
// src/sql/select.ts
|
|
306
|
-
function buildColumnRef(name) {
|
|
307
|
-
const snakeName = camelToSnake(name);
|
|
400
|
+
function buildColumnRef(name, casingOverrides) {
|
|
401
|
+
const snakeName = camelToSnake(name, casingOverrides);
|
|
308
402
|
if (snakeName === name) {
|
|
309
403
|
return `"${name}"`;
|
|
310
404
|
}
|
|
311
405
|
return `"${snakeName}" AS "${name}"`;
|
|
312
406
|
}
|
|
313
|
-
function buildSelect(options) {
|
|
407
|
+
function buildSelect(options, dialect = options.dialect ?? defaultPostgresDialect) {
|
|
314
408
|
const parts = [];
|
|
315
409
|
const allParams = [];
|
|
410
|
+
const { casingOverrides } = options;
|
|
316
411
|
let columnList;
|
|
317
412
|
if (options.columns && options.columns.length > 0) {
|
|
318
|
-
columnList = options.columns.map(buildColumnRef).join(", ");
|
|
413
|
+
columnList = options.columns.map((col) => buildColumnRef(col, casingOverrides)).join(", ");
|
|
319
414
|
} else {
|
|
320
415
|
columnList = "*";
|
|
321
416
|
}
|
|
@@ -325,7 +420,7 @@ function buildSelect(options) {
|
|
|
325
420
|
parts.push(`SELECT ${columnList} FROM "${options.table}"`);
|
|
326
421
|
const whereClauses = [];
|
|
327
422
|
if (options.where) {
|
|
328
|
-
const whereResult = buildWhere(options.where);
|
|
423
|
+
const whereResult = buildWhere(options.where, 0, casingOverrides, dialect);
|
|
329
424
|
if (whereResult.sql.length > 0) {
|
|
330
425
|
whereClauses.push(whereResult.sql);
|
|
331
426
|
allParams.push(...whereResult.params);
|
|
@@ -335,11 +430,11 @@ function buildSelect(options) {
|
|
|
335
430
|
const cursorEntries = Object.entries(options.cursor);
|
|
336
431
|
if (cursorEntries.length === 1) {
|
|
337
432
|
const [col, value] = cursorEntries[0];
|
|
338
|
-
const snakeCol = camelToSnake(col);
|
|
433
|
+
const snakeCol = camelToSnake(col, casingOverrides);
|
|
339
434
|
const dir = options.orderBy?.[col] ?? "asc";
|
|
340
435
|
const op = dir === "desc" ? "<" : ">";
|
|
341
436
|
allParams.push(value);
|
|
342
|
-
whereClauses.push(`"${snakeCol}" ${op}
|
|
437
|
+
whereClauses.push(`"${snakeCol}" ${op} ${dialect.param(allParams.length)}`);
|
|
343
438
|
} else if (cursorEntries.length > 1) {
|
|
344
439
|
const cols = [];
|
|
345
440
|
const placeholders = [];
|
|
@@ -347,9 +442,9 @@ function buildSelect(options) {
|
|
|
347
442
|
const dir = options.orderBy?.[firstCol] ?? "asc";
|
|
348
443
|
const op = dir === "desc" ? "<" : ">";
|
|
349
444
|
for (const [col, value] of cursorEntries) {
|
|
350
|
-
cols.push(`"${camelToSnake(col)}"`);
|
|
445
|
+
cols.push(`"${camelToSnake(col, casingOverrides)}"`);
|
|
351
446
|
allParams.push(value);
|
|
352
|
-
placeholders.push(
|
|
447
|
+
placeholders.push(dialect.param(allParams.length));
|
|
353
448
|
}
|
|
354
449
|
whereClauses.push(`(${cols.join(", ")}) ${op} (${placeholders.join(", ")})`);
|
|
355
450
|
}
|
|
@@ -358,12 +453,12 @@ function buildSelect(options) {
|
|
|
358
453
|
parts.push(`WHERE ${whereClauses.join(" AND ")}`);
|
|
359
454
|
}
|
|
360
455
|
if (options.orderBy) {
|
|
361
|
-
const orderClauses = Object.entries(options.orderBy).map(([col, dir]) => `"${camelToSnake(col)}" ${dir.toUpperCase()}`);
|
|
456
|
+
const orderClauses = Object.entries(options.orderBy).map(([col, dir]) => `"${camelToSnake(col, casingOverrides)}" ${dir.toUpperCase()}`);
|
|
362
457
|
if (orderClauses.length > 0) {
|
|
363
458
|
parts.push(`ORDER BY ${orderClauses.join(", ")}`);
|
|
364
459
|
}
|
|
365
460
|
} else if (options.cursor) {
|
|
366
|
-
const orderClauses = Object.keys(options.cursor).map((col) => `"${camelToSnake(col)}" ASC`);
|
|
461
|
+
const orderClauses = Object.keys(options.cursor).map((col) => `"${camelToSnake(col, casingOverrides)}" ASC`);
|
|
367
462
|
if (orderClauses.length > 0) {
|
|
368
463
|
parts.push(`ORDER BY ${orderClauses.join(", ")}`);
|
|
369
464
|
}
|
|
@@ -371,11 +466,11 @@ function buildSelect(options) {
|
|
|
371
466
|
const effectiveLimit = options.take ?? options.limit;
|
|
372
467
|
if (effectiveLimit !== undefined) {
|
|
373
468
|
allParams.push(effectiveLimit);
|
|
374
|
-
parts.push(`LIMIT
|
|
469
|
+
parts.push(`LIMIT ${dialect.param(allParams.length)}`);
|
|
375
470
|
}
|
|
376
471
|
if (options.offset !== undefined) {
|
|
377
472
|
allParams.push(options.offset);
|
|
378
|
-
parts.push(`OFFSET
|
|
473
|
+
parts.push(`OFFSET ${dialect.param(allParams.length)}`);
|
|
379
474
|
}
|
|
380
475
|
return {
|
|
381
476
|
sql: parts.join(" "),
|
|
@@ -391,7 +486,7 @@ function buildReturningColumnRef3(name) {
|
|
|
391
486
|
}
|
|
392
487
|
return `"${snakeName}" AS "${name}"`;
|
|
393
488
|
}
|
|
394
|
-
function buildUpdate(options) {
|
|
489
|
+
function buildUpdate(options, dialect = options.dialect ?? defaultPostgresDialect) {
|
|
395
490
|
const keys = Object.keys(options.data);
|
|
396
491
|
const nowSet = new Set(options.nowColumns ?? []);
|
|
397
492
|
const allParams = [];
|
|
@@ -400,15 +495,15 @@ function buildUpdate(options) {
|
|
|
400
495
|
const snakeCol = camelToSnake(key);
|
|
401
496
|
const value = options.data[key];
|
|
402
497
|
if (nowSet.has(key) && value === "now") {
|
|
403
|
-
setClauses.push(`"${snakeCol}" =
|
|
498
|
+
setClauses.push(`"${snakeCol}" = ${dialect.now()}`);
|
|
404
499
|
} else {
|
|
405
500
|
allParams.push(value);
|
|
406
|
-
setClauses.push(`"${snakeCol}" =
|
|
501
|
+
setClauses.push(`"${snakeCol}" = ${dialect.param(allParams.length)}`);
|
|
407
502
|
}
|
|
408
503
|
}
|
|
409
504
|
let sql = `UPDATE "${options.table}" SET ${setClauses.join(", ")}`;
|
|
410
505
|
if (options.where) {
|
|
411
|
-
const whereResult = buildWhere(options.where, allParams.length);
|
|
506
|
+
const whereResult = buildWhere(options.where, allParams.length, undefined, dialect);
|
|
412
507
|
if (whereResult.sql.length > 0) {
|
|
413
508
|
sql += ` WHERE ${whereResult.sql}`;
|
|
414
509
|
allParams.push(...whereResult.params);
|
|
@@ -425,4 +520,4 @@ function buildUpdate(options) {
|
|
|
425
520
|
return { sql, params: allParams };
|
|
426
521
|
}
|
|
427
522
|
|
|
428
|
-
export { buildWhere, buildDelete, buildInsert, buildSelect, buildUpdate };
|
|
523
|
+
export { PostgresDialect, defaultPostgresDialect, SqliteDialect, defaultSqliteDialect, buildWhere, buildDelete, buildInsert, buildSelect, buildUpdate };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
snakeToCamel
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-v2qm94qp.js";
|
|
4
4
|
|
|
5
5
|
// src/errors/db-error.ts
|
|
6
6
|
class DbError extends Error {
|
|
@@ -111,7 +111,7 @@ class CheckConstraintError extends DbError {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
class NotFoundError extends DbError {
|
|
114
|
-
code = "
|
|
114
|
+
code = "NotFound";
|
|
115
115
|
table;
|
|
116
116
|
query;
|
|
117
117
|
constructor(table, query) {
|
|
@@ -239,21 +239,16 @@ function getColumnNames(table) {
|
|
|
239
239
|
return Object.keys(table._columns);
|
|
240
240
|
}
|
|
241
241
|
function getDefaultColumns(table) {
|
|
242
|
-
return
|
|
243
|
-
const col = table._columns[key];
|
|
244
|
-
return col ? !col._meta.hidden : true;
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
function getNotSensitiveColumns(table) {
|
|
248
|
-
return Object.keys(table._columns).filter((key) => {
|
|
249
|
-
const col = table._columns[key];
|
|
250
|
-
return col ? !col._meta.sensitive && !col._meta.hidden : true;
|
|
251
|
-
});
|
|
242
|
+
return getColumnsWithoutAnnotations(table, []);
|
|
252
243
|
}
|
|
253
|
-
function
|
|
244
|
+
function getColumnsWithoutAnnotations(table, annotations) {
|
|
245
|
+
const allAnnotations = annotations.includes("hidden") ? annotations : [...annotations, "hidden"];
|
|
254
246
|
return Object.keys(table._columns).filter((key) => {
|
|
255
247
|
const col = table._columns[key];
|
|
256
|
-
|
|
248
|
+
if (!col)
|
|
249
|
+
return true;
|
|
250
|
+
const colAnnotations = col._meta._annotations;
|
|
251
|
+
return !allAnnotations.some((f) => colAnnotations[f]);
|
|
257
252
|
});
|
|
258
253
|
}
|
|
259
254
|
function resolveSelectColumns(table, select) {
|
|
@@ -261,10 +256,9 @@ function resolveSelectColumns(table, select) {
|
|
|
261
256
|
return getDefaultColumns(table);
|
|
262
257
|
}
|
|
263
258
|
if ("not" in select && select.not !== undefined) {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
return getNotHiddenColumns(table);
|
|
259
|
+
const notValue = select.not;
|
|
260
|
+
const flags = Array.isArray(notValue) ? notValue : [notValue];
|
|
261
|
+
return getColumnsWithoutAnnotations(table, flags);
|
|
268
262
|
}
|
|
269
263
|
return Object.keys(select).filter((k) => select[k] === true);
|
|
270
264
|
}
|
|
@@ -280,6 +274,18 @@ function getPrimaryKeyColumns(table) {
|
|
|
280
274
|
return col ? col._meta.primary : false;
|
|
281
275
|
});
|
|
282
276
|
}
|
|
277
|
+
function getReadOnlyColumns(table) {
|
|
278
|
+
return Object.keys(table._columns).filter((key) => {
|
|
279
|
+
const col = table._columns[key];
|
|
280
|
+
return col ? col._meta.isReadOnly : false;
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
function getAutoUpdateColumns(table) {
|
|
284
|
+
return Object.keys(table._columns).filter((key) => {
|
|
285
|
+
const col = table._columns[key];
|
|
286
|
+
return col ? col._meta.isAutoUpdate : false;
|
|
287
|
+
});
|
|
288
|
+
}
|
|
283
289
|
|
|
284
290
|
// src/query/row-mapper.ts
|
|
285
291
|
function mapRow(row) {
|
|
@@ -293,4 +299,4 @@ function mapRows(rows) {
|
|
|
293
299
|
return rows.map((row) => mapRow(row));
|
|
294
300
|
}
|
|
295
301
|
|
|
296
|
-
export { DbError, UniqueConstraintError, ForeignKeyError, NotNullError, CheckConstraintError, NotFoundError, ConnectionError, ConnectionPoolExhaustedError, parsePgError, executeQuery, getColumnNames, getDefaultColumns,
|
|
302
|
+
export { DbError, UniqueConstraintError, ForeignKeyError, NotNullError, CheckConstraintError, NotFoundError, ConnectionError, ConnectionPoolExhaustedError, parsePgError, executeQuery, getColumnNames, getDefaultColumns, getColumnsWithoutAnnotations, resolveSelectColumns, getTimestampColumns, getPrimaryKeyColumns, getReadOnlyColumns, getAutoUpdateColumns, mapRow, mapRows };
|
|
@@ -44,7 +44,7 @@ var ERROR_PATTERNS = [
|
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
46
|
pattern: /Record not found in table ([^\s]+)/,
|
|
47
|
-
code: "
|
|
47
|
+
code: "NotFound",
|
|
48
48
|
explanation: "A getOrThrow, update, or delete query did not match any rows.",
|
|
49
49
|
suggestion: "Verify the where clause matches existing rows. Use get() if the record may not exist."
|
|
50
50
|
},
|
|
@@ -52,7 +52,7 @@ var ERROR_PATTERNS = [
|
|
|
52
52
|
pattern: /Table "([^"]+)" is not registered in the database/,
|
|
53
53
|
code: "UNREGISTERED_TABLE",
|
|
54
54
|
explanation: "The table name passed to a query method is not in the database registry.",
|
|
55
|
-
suggestion: "Register the table in the `
|
|
55
|
+
suggestion: "Register the table in the `models` record passed to `createDb()`."
|
|
56
56
|
}
|
|
57
57
|
];
|
|
58
58
|
function diagnoseError(message) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// src/migration/snapshot-storage.ts
|
|
2
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
|
|
5
|
+
class NodeSnapshotStorage {
|
|
6
|
+
async load(path) {
|
|
7
|
+
try {
|
|
8
|
+
const content = await readFile(path, "utf-8");
|
|
9
|
+
return JSON.parse(content);
|
|
10
|
+
} catch (err) {
|
|
11
|
+
if (err.code === "ENOENT") {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
throw err;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
async save(path, snapshot) {
|
|
18
|
+
const dir = dirname(path);
|
|
19
|
+
await mkdir(dir, { recursive: true });
|
|
20
|
+
const content = JSON.stringify(snapshot, null, 2);
|
|
21
|
+
await writeFile(path, `${content}
|
|
22
|
+
`, "utf-8");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { NodeSnapshotStorage };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BaseSqlAdapter,
|
|
3
|
+
generateCreateTableSql,
|
|
4
|
+
generateIndexSql
|
|
5
|
+
} from "./chunk-sfmyxz6r.js";
|
|
6
|
+
|
|
7
|
+
// src/adapters/d1-adapter.ts
|
|
8
|
+
function createD1Driver(d1) {
|
|
9
|
+
const query = async (sql, params) => {
|
|
10
|
+
const prepared = d1.prepare(sql);
|
|
11
|
+
const bound = params ? prepared.bind(...params) : prepared;
|
|
12
|
+
const result = await bound.all();
|
|
13
|
+
return result.results;
|
|
14
|
+
};
|
|
15
|
+
const execute = async (sql, params) => {
|
|
16
|
+
const prepared = d1.prepare(sql);
|
|
17
|
+
const bound = params ? prepared.bind(...params) : prepared;
|
|
18
|
+
const result = await bound.run();
|
|
19
|
+
return { rowsAffected: result.meta.changes };
|
|
20
|
+
};
|
|
21
|
+
return {
|
|
22
|
+
query,
|
|
23
|
+
execute,
|
|
24
|
+
close: async () => {}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
class D1Adapter extends BaseSqlAdapter {
|
|
29
|
+
constructor(driver, schema) {
|
|
30
|
+
super(driver, schema);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function createD1Adapter(options) {
|
|
34
|
+
const { schema, d1, migrations } = options;
|
|
35
|
+
const driver = createD1Driver(d1);
|
|
36
|
+
if (migrations?.autoApply) {
|
|
37
|
+
const createTableSql = generateCreateTableSql(schema);
|
|
38
|
+
driver.execute(createTableSql);
|
|
39
|
+
const indexSqls = generateIndexSql(schema);
|
|
40
|
+
for (const sql of indexSqls) {
|
|
41
|
+
driver.execute(sql);
|
|
42
|
+
}
|
|
43
|
+
console.log(`\uD83D\uDCE6 D1 database adapter initialized for table: ${schema._name}`);
|
|
44
|
+
}
|
|
45
|
+
return new D1Adapter(driver, schema);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export { createD1Driver, createD1Adapter };
|