@dockstat/sqlite-wrapper 1.2.6 → 1.2.8
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/LICENSE +373 -373
- package/README.md +99 -66
- package/index.ts +858 -840
- package/package.json +54 -54
- package/query-builder/base.ts +221 -221
- package/query-builder/delete.ts +352 -352
- package/query-builder/index.ts +431 -431
- package/query-builder/insert.ts +249 -249
- package/query-builder/select.ts +358 -358
- package/query-builder/update.ts +278 -278
- package/query-builder/where.ts +307 -307
- package/types.ts +623 -623
package/types.ts
CHANGED
|
@@ -1,623 +1,623 @@
|
|
|
1
|
-
import type { Database, SQLQueryBindings } from "bun:sqlite";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* All SQLite data types including affinity types
|
|
5
|
-
*/
|
|
6
|
-
export const SQLiteTypes = {
|
|
7
|
-
// Standard SQLite types
|
|
8
|
-
INTEGER: "INTEGER" as const,
|
|
9
|
-
TEXT: "TEXT" as const,
|
|
10
|
-
REAL: "REAL" as const,
|
|
11
|
-
BLOB: "BLOB" as const,
|
|
12
|
-
NUMERIC: "NUMERIC" as const,
|
|
13
|
-
|
|
14
|
-
// Common type aliases and variations
|
|
15
|
-
INT: "INT" as const,
|
|
16
|
-
TINYINT: "TINYINT" as const,
|
|
17
|
-
SMALLINT: "SMALLINT" as const,
|
|
18
|
-
MEDIUMINT: "MEDIUMINT" as const,
|
|
19
|
-
BIGINT: "BIGINT" as const,
|
|
20
|
-
|
|
21
|
-
// Text variations
|
|
22
|
-
VARCHAR: "VARCHAR" as const,
|
|
23
|
-
CHAR: "CHAR" as const,
|
|
24
|
-
CHARACTER: "CHARACTER" as const,
|
|
25
|
-
NCHAR: "NCHAR" as const,
|
|
26
|
-
NVARCHAR: "NVARCHAR" as const,
|
|
27
|
-
CLOB: "CLOB" as const,
|
|
28
|
-
|
|
29
|
-
// Numeric variations
|
|
30
|
-
DOUBLE: "DOUBLE" as const,
|
|
31
|
-
FLOAT: "FLOAT" as const,
|
|
32
|
-
DECIMAL: "DECIMAL" as const,
|
|
33
|
-
|
|
34
|
-
// Date/Time (stored as TEXT, INTEGER, or REAL)
|
|
35
|
-
DATE: "DATE" as const,
|
|
36
|
-
DATETIME: "DATETIME" as const,
|
|
37
|
-
TIMESTAMP: "TIMESTAMP" as const,
|
|
38
|
-
TIME: "TIME" as const,
|
|
39
|
-
|
|
40
|
-
// Boolean (stored as INTEGER)
|
|
41
|
-
BOOLEAN: "BOOLEAN" as const,
|
|
42
|
-
|
|
43
|
-
// JSON (stored as TEXT)
|
|
44
|
-
JSON: "JSON" as const,
|
|
45
|
-
} as const;
|
|
46
|
-
|
|
47
|
-
export type SQLiteType = (typeof SQLiteTypes)[keyof typeof SQLiteTypes];
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* SQLite built-in scalar functions
|
|
51
|
-
*/
|
|
52
|
-
export const SQLiteFunctions = {
|
|
53
|
-
// Date/Time functions
|
|
54
|
-
DATE: (expr: string) => `DATE(${expr})`,
|
|
55
|
-
TIME: (expr: string) => `TIME(${expr})`,
|
|
56
|
-
DATETIME: (expr: string) => `DATETIME(${expr})`,
|
|
57
|
-
JULIANDAY: (expr: string) => `JULIANDAY(${expr})`,
|
|
58
|
-
STRFTIME: (format: string, expr: string) => `STRFTIME('${format}', ${expr})`,
|
|
59
|
-
|
|
60
|
-
// String functions
|
|
61
|
-
LENGTH: (expr: string) => `LENGTH(${expr})`,
|
|
62
|
-
LOWER: (expr: string) => `LOWER(${expr})`,
|
|
63
|
-
UPPER: (expr: string) => `UPPER(${expr})`,
|
|
64
|
-
TRIM: (expr: string, chars?: string) =>
|
|
65
|
-
chars ? `TRIM(${expr}, '${chars}')` : `TRIM(${expr})`,
|
|
66
|
-
LTRIM: (expr: string, chars?: string) =>
|
|
67
|
-
chars ? `LTRIM(${expr}, '${chars}')` : `LTRIM(${expr})`,
|
|
68
|
-
RTRIM: (expr: string, chars?: string) =>
|
|
69
|
-
chars ? `RTRIM(${expr}, '${chars}')` : `RTRIM(${expr})`,
|
|
70
|
-
SUBSTR: (expr: string, start: number, length?: number) =>
|
|
71
|
-
length
|
|
72
|
-
? `SUBSTR(${expr}, ${start}, ${length})`
|
|
73
|
-
: `SUBSTR(${expr}, ${start})`,
|
|
74
|
-
SUBSTRING: (expr: string, start: number, length?: number) =>
|
|
75
|
-
length
|
|
76
|
-
? `SUBSTRING(${expr}, ${start}, ${length})`
|
|
77
|
-
: `SUBSTRING(${expr}, ${start})`,
|
|
78
|
-
REPLACE: (expr: string, old: string, replacement: string) =>
|
|
79
|
-
`REPLACE(${expr}, '${old}', '${replacement}')`,
|
|
80
|
-
PRINTF: (format: string, ...args: string[]) =>
|
|
81
|
-
`PRINTF('${format}', ${args.join(", ")})`,
|
|
82
|
-
|
|
83
|
-
// Math functions
|
|
84
|
-
ABS: (expr: string) => `ABS(${expr})`,
|
|
85
|
-
ROUND: (expr: string, digits?: number) =>
|
|
86
|
-
digits !== undefined ? `ROUND(${expr}, ${digits})` : `ROUND(${expr})`,
|
|
87
|
-
RANDOM: () => "RANDOM()",
|
|
88
|
-
MIN: (...exprs: string[]) => `MIN(${exprs.join(", ")})`,
|
|
89
|
-
MAX: (...exprs: string[]) => `MAX(${exprs.join(", ")})`,
|
|
90
|
-
|
|
91
|
-
// Type conversion
|
|
92
|
-
CAST: (expr: string, type: string) => `CAST(${expr} AS ${type})`,
|
|
93
|
-
TYPEOF: (expr: string) => `TYPEOF(${expr})`,
|
|
94
|
-
|
|
95
|
-
// Conditional
|
|
96
|
-
COALESCE: (...exprs: string[]) => `COALESCE(${exprs.join(", ")})`,
|
|
97
|
-
IFNULL: (expr: string, replacement: string) =>
|
|
98
|
-
`IFNULL(${expr}, ${replacement})`,
|
|
99
|
-
NULLIF: (expr1: string, expr2: string) => `NULLIF(${expr1}, ${expr2})`,
|
|
100
|
-
IIF: (condition: string, trueValue: string, falseValue: string) =>
|
|
101
|
-
`IIF(${condition}, ${trueValue}, ${falseValue})`,
|
|
102
|
-
|
|
103
|
-
// Aggregate functions (for use in expressions)
|
|
104
|
-
COUNT: (expr?: string) => (expr ? `COUNT(${expr})` : "COUNT(*)"),
|
|
105
|
-
SUM: (expr: string) => `SUM(${expr})`,
|
|
106
|
-
AVG: (expr: string) => `AVG(${expr})`,
|
|
107
|
-
TOTAL: (expr: string) => `TOTAL(${expr})`,
|
|
108
|
-
GROUP_CONCAT: (expr: string, separator?: string) =>
|
|
109
|
-
separator
|
|
110
|
-
? `GROUP_CONCAT(${expr}, '${separator}')`
|
|
111
|
-
: `GROUP_CONCAT(${expr})`,
|
|
112
|
-
|
|
113
|
-
// JSON functions (SQLite 3.45+)
|
|
114
|
-
JSON: (expr: string) => `JSON(${expr})`,
|
|
115
|
-
JSON_EXTRACT: (json: string, path: string) =>
|
|
116
|
-
`JSON_EXTRACT(${json}, '${path}')`,
|
|
117
|
-
JSON_TYPE: (json: string, path?: string) =>
|
|
118
|
-
path ? `JSON_TYPE(${json}, '${path}')` : `JSON_TYPE(${json})`,
|
|
119
|
-
JSON_VALID: (expr: string) => `JSON_VALID(${expr})`,
|
|
120
|
-
JSON_ARRAY: (...values: string[]) => `JSON_ARRAY(${values.join(", ")})`,
|
|
121
|
-
JSON_OBJECT: (...pairs: string[]) => `JSON_OBJECT(${pairs.join(", ")})`,
|
|
122
|
-
} as const;
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* SQLite keywords and special values
|
|
126
|
-
*/
|
|
127
|
-
export const SQLiteKeywords = {
|
|
128
|
-
// Special values
|
|
129
|
-
NULL: "NULL" as const,
|
|
130
|
-
CURRENT_TIME: "CURRENT_TIME" as const,
|
|
131
|
-
CURRENT_DATE: "CURRENT_DATE" as const,
|
|
132
|
-
CURRENT_TIMESTAMP: "CURRENT_TIMESTAMP" as const,
|
|
133
|
-
|
|
134
|
-
// Boolean values (as integers)
|
|
135
|
-
TRUE: "1" as const,
|
|
136
|
-
FALSE: "0" as const,
|
|
137
|
-
|
|
138
|
-
// Conflict resolution
|
|
139
|
-
ROLLBACK: "ROLLBACK" as const,
|
|
140
|
-
ABORT: "ABORT" as const,
|
|
141
|
-
FAIL: "FAIL" as const,
|
|
142
|
-
IGNORE: "IGNORE" as const,
|
|
143
|
-
REPLACE: "REPLACE" as const,
|
|
144
|
-
} as const;
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Foreign key actions
|
|
148
|
-
*/
|
|
149
|
-
export type ForeignKeyAction =
|
|
150
|
-
| "CASCADE"
|
|
151
|
-
| "SET NULL"
|
|
152
|
-
| "RESTRICT"
|
|
153
|
-
| "NO ACTION"
|
|
154
|
-
| "SET DEFAULT";
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Column constraint options with comprehensive support
|
|
158
|
-
*/
|
|
159
|
-
export interface ColumnConstraints {
|
|
160
|
-
/** Column is PRIMARY KEY */
|
|
161
|
-
primaryKey?: boolean;
|
|
162
|
-
/** Column has AUTOINCREMENT (only valid with INTEGER PRIMARY KEY) */
|
|
163
|
-
autoincrement?: boolean;
|
|
164
|
-
/** Column is NOT NULL */
|
|
165
|
-
notNull?: boolean;
|
|
166
|
-
/** Column has UNIQUE constraint */
|
|
167
|
-
unique?: boolean;
|
|
168
|
-
/** Default value - can be literal value, function call, or keyword */
|
|
169
|
-
default?: string | number | boolean | null | DefaultExpression;
|
|
170
|
-
/** Custom check constraint */
|
|
171
|
-
check?: string;
|
|
172
|
-
/** Collation sequence */
|
|
173
|
-
collate?: "BINARY" | "NOCASE" | "RTRIM" | string;
|
|
174
|
-
/** References another table (foreign key) */
|
|
175
|
-
references?: {
|
|
176
|
-
table: string;
|
|
177
|
-
column: string;
|
|
178
|
-
onDelete?: ForeignKeyAction;
|
|
179
|
-
onUpdate?: ForeignKeyAction;
|
|
180
|
-
};
|
|
181
|
-
/** Generated column expression (GENERATED ALWAYS AS) */
|
|
182
|
-
generated?: {
|
|
183
|
-
expression: string;
|
|
184
|
-
stored?: boolean; // true for STORED, false/undefined for VIRTUAL
|
|
185
|
-
};
|
|
186
|
-
/** Column comment (stored as metadata, not in schema) */
|
|
187
|
-
comment?: string;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Type-safe default value expressions
|
|
192
|
-
*/
|
|
193
|
-
export type DefaultExpression = {
|
|
194
|
-
_type: "expression";
|
|
195
|
-
expression: string;
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Helper to create default expressions
|
|
200
|
-
*/
|
|
201
|
-
export const defaultExpr = (expression: string): DefaultExpression => ({
|
|
202
|
-
_type: "expression",
|
|
203
|
-
expression,
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Type-safe column definition
|
|
208
|
-
*/
|
|
209
|
-
export interface ColumnDefinition extends ColumnConstraints {
|
|
210
|
-
/** SQLite data type */
|
|
211
|
-
type: SQLiteType;
|
|
212
|
-
/** Optional type parameters (e.g., VARCHAR(255)) */
|
|
213
|
-
length?: number;
|
|
214
|
-
/** Precision for DECIMAL/NUMERIC types */
|
|
215
|
-
precision?: number;
|
|
216
|
-
/** Scale for DECIMAL/NUMERIC types */
|
|
217
|
-
scale?: number;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Type-safe table schema definition
|
|
222
|
-
*/
|
|
223
|
-
export type TableSchema = Record<string, ColumnDefinition>;
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
export type TypedTableSchema<T extends string = string> = Record<T, ColumnDefinition>;
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Table constraint types
|
|
230
|
-
*/
|
|
231
|
-
export interface TableConstraints {
|
|
232
|
-
/** PRIMARY KEY constraint on multiple columns */
|
|
233
|
-
primaryKey?: string[];
|
|
234
|
-
/** UNIQUE constraints */
|
|
235
|
-
unique?: string[] | string[][];
|
|
236
|
-
/** CHECK constraints */
|
|
237
|
-
check?: string[];
|
|
238
|
-
/** FOREIGN KEY constraints */
|
|
239
|
-
foreignKeys?: Array<{
|
|
240
|
-
columns: string[];
|
|
241
|
-
references: {
|
|
242
|
-
table: string;
|
|
243
|
-
columns: string[];
|
|
244
|
-
onDelete?: ForeignKeyAction;
|
|
245
|
-
onUpdate?: ForeignKeyAction;
|
|
246
|
-
};
|
|
247
|
-
}>;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Enhanced table options
|
|
252
|
-
*/
|
|
253
|
-
export interface TableOptions<T> {
|
|
254
|
-
/** Add IF NOT EXISTS clause */
|
|
255
|
-
ifNotExists?: boolean;
|
|
256
|
-
/** Create WITHOUT ROWID table */
|
|
257
|
-
withoutRowId?: boolean;
|
|
258
|
-
/** Table constraints */
|
|
259
|
-
constraints?: TableConstraints;
|
|
260
|
-
/** Temporary table */
|
|
261
|
-
temporary?: boolean;
|
|
262
|
-
/** Table comment */
|
|
263
|
-
comment?: string;
|
|
264
|
-
|
|
265
|
-
jsonConfig?: JsonColumnConfig<T>
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Comprehensive column helper functions with full type support
|
|
270
|
-
*/
|
|
271
|
-
export const column = {
|
|
272
|
-
/**
|
|
273
|
-
* Create an INTEGER column with optional size specification
|
|
274
|
-
*/
|
|
275
|
-
integer: (
|
|
276
|
-
constraints?: ColumnConstraints & {
|
|
277
|
-
size?: "TINYINT" | "SMALLINT" | "MEDIUMINT" | "BIGINT";
|
|
278
|
-
},
|
|
279
|
-
): ColumnDefinition => ({
|
|
280
|
-
type: constraints?.size || SQLiteTypes.INTEGER,
|
|
281
|
-
...constraints,
|
|
282
|
-
}),
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Create a TEXT column with optional length
|
|
286
|
-
*/
|
|
287
|
-
text: (
|
|
288
|
-
constraints?: ColumnConstraints & {
|
|
289
|
-
length?: number;
|
|
290
|
-
variant?: "VARCHAR" | "CHAR" | "CLOB" | "NCHAR" | "NVARCHAR";
|
|
291
|
-
},
|
|
292
|
-
): ColumnDefinition => ({
|
|
293
|
-
type: constraints?.variant || SQLiteTypes.TEXT,
|
|
294
|
-
length: constraints?.length,
|
|
295
|
-
...constraints,
|
|
296
|
-
}),
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Create a REAL column
|
|
300
|
-
*/
|
|
301
|
-
real: (
|
|
302
|
-
constraints?: ColumnConstraints & { variant?: "DOUBLE" | "FLOAT" },
|
|
303
|
-
): ColumnDefinition => ({
|
|
304
|
-
type: constraints?.variant || SQLiteTypes.REAL,
|
|
305
|
-
...constraints,
|
|
306
|
-
}),
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* Create a BLOB column
|
|
310
|
-
*/
|
|
311
|
-
blob: (constraints?: ColumnConstraints): ColumnDefinition => ({
|
|
312
|
-
type: SQLiteTypes.BLOB,
|
|
313
|
-
...constraints,
|
|
314
|
-
}),
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Create a NUMERIC/DECIMAL column with precision and scale
|
|
318
|
-
*/
|
|
319
|
-
numeric: (
|
|
320
|
-
constraints?: ColumnConstraints & {
|
|
321
|
-
precision?: number;
|
|
322
|
-
scale?: number;
|
|
323
|
-
variant?: "DECIMAL";
|
|
324
|
-
},
|
|
325
|
-
): ColumnDefinition => ({
|
|
326
|
-
type: constraints?.variant || SQLiteTypes.NUMERIC,
|
|
327
|
-
precision: constraints?.precision,
|
|
328
|
-
scale: constraints?.scale,
|
|
329
|
-
...constraints,
|
|
330
|
-
}),
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* Create a DATE column (stored as TEXT)
|
|
334
|
-
*/
|
|
335
|
-
date: (constraints?: ColumnConstraints): ColumnDefinition => ({
|
|
336
|
-
type: SQLiteTypes.DATE,
|
|
337
|
-
...constraints,
|
|
338
|
-
}),
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Create a DATETIME column (stored as TEXT)
|
|
342
|
-
*/
|
|
343
|
-
datetime: (constraints?: ColumnConstraints): ColumnDefinition => ({
|
|
344
|
-
type: SQLiteTypes.DATETIME,
|
|
345
|
-
...constraints,
|
|
346
|
-
}),
|
|
347
|
-
|
|
348
|
-
/**
|
|
349
|
-
* Create a TIMESTAMP column (stored as INTEGER by default)
|
|
350
|
-
*/
|
|
351
|
-
timestamp: (
|
|
352
|
-
constraints?: ColumnConstraints & { asText?: boolean },
|
|
353
|
-
): ColumnDefinition => ({
|
|
354
|
-
type: constraints?.asText ? SQLiteTypes.TEXT : SQLiteTypes.INTEGER,
|
|
355
|
-
...constraints,
|
|
356
|
-
}),
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* Create a TIME column (stored as TEXT)
|
|
360
|
-
*/
|
|
361
|
-
time: (constraints?: ColumnConstraints): ColumnDefinition => ({
|
|
362
|
-
type: SQLiteTypes.TIME,
|
|
363
|
-
...constraints,
|
|
364
|
-
}),
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* Create a BOOLEAN column (stored as INTEGER)
|
|
368
|
-
*/
|
|
369
|
-
boolean: (constraints?: ColumnConstraints): ColumnDefinition => ({
|
|
370
|
-
type: SQLiteTypes.BOOLEAN,
|
|
371
|
-
check: constraints?.check || "{{COLUMN}} IN (0, 1)", // Placeholder for column name
|
|
372
|
-
...constraints,
|
|
373
|
-
}),
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* Create a JSON column (stored as TEXT)
|
|
377
|
-
*/
|
|
378
|
-
json: (
|
|
379
|
-
constraints: ColumnConstraints & { validateJson?: boolean },
|
|
380
|
-
): ColumnDefinition => ({
|
|
381
|
-
type: SQLiteTypes.JSON,
|
|
382
|
-
check: constraints?.validateJson
|
|
383
|
-
? "JSON_VALID({{COLUMN}})"
|
|
384
|
-
: constraints?.check,
|
|
385
|
-
...constraints,
|
|
386
|
-
}),
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* Create a VARCHAR column with specified length
|
|
390
|
-
*/
|
|
391
|
-
varchar: (
|
|
392
|
-
length: number,
|
|
393
|
-
constraints?: ColumnConstraints,
|
|
394
|
-
): ColumnDefinition => ({
|
|
395
|
-
type: SQLiteTypes.VARCHAR,
|
|
396
|
-
length,
|
|
397
|
-
...constraints,
|
|
398
|
-
}),
|
|
399
|
-
|
|
400
|
-
/**
|
|
401
|
-
* Create a CHAR column with specified length
|
|
402
|
-
*/
|
|
403
|
-
char: (
|
|
404
|
-
length: number,
|
|
405
|
-
constraints?: ColumnConstraints,
|
|
406
|
-
): ColumnDefinition => ({
|
|
407
|
-
type: SQLiteTypes.CHAR,
|
|
408
|
-
length,
|
|
409
|
-
...constraints,
|
|
410
|
-
}),
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
* Create an auto-incrementing primary key column
|
|
414
|
-
*/
|
|
415
|
-
id: (
|
|
416
|
-
constraints?: Omit<
|
|
417
|
-
ColumnConstraints,
|
|
418
|
-
"primaryKey" | "autoincrement" | "notNull"
|
|
419
|
-
>,
|
|
420
|
-
): ColumnDefinition => ({
|
|
421
|
-
type: SQLiteTypes.INTEGER,
|
|
422
|
-
primaryKey: true,
|
|
423
|
-
autoincrement: true,
|
|
424
|
-
notNull: true,
|
|
425
|
-
...constraints,
|
|
426
|
-
}),
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
* Create a UUID column (stored as TEXT)
|
|
430
|
-
*/
|
|
431
|
-
uuid: (
|
|
432
|
-
constraints?: ColumnConstraints & { generateDefault?: boolean },
|
|
433
|
-
): ColumnDefinition => ({
|
|
434
|
-
type: SQLiteTypes.TEXT,
|
|
435
|
-
length: 36,
|
|
436
|
-
default: constraints?.generateDefault
|
|
437
|
-
? defaultExpr(
|
|
438
|
-
"lower(hex(randomblob(4))) || '-' || lower(hex(randomblob(2))) || '-4' || substr(lower(hex(randomblob(2))),2) || '-' || substr('89ab',abs(random()) % 4 + 1, 1) || substr(lower(hex(randomblob(2))),2) || '-' || lower(hex(randomblob(6)))",
|
|
439
|
-
)
|
|
440
|
-
: constraints?.default,
|
|
441
|
-
...constraints,
|
|
442
|
-
}),
|
|
443
|
-
|
|
444
|
-
/**
|
|
445
|
-
* Create a created_at timestamp column
|
|
446
|
-
*/
|
|
447
|
-
createdAt: (
|
|
448
|
-
constraints?: Omit<ColumnConstraints, "default" | "notNull"> & {
|
|
449
|
-
asText?: boolean;
|
|
450
|
-
},
|
|
451
|
-
): ColumnDefinition => ({
|
|
452
|
-
type: constraints?.asText ? SQLiteTypes.DATETIME : SQLiteTypes.INTEGER,
|
|
453
|
-
notNull: true,
|
|
454
|
-
default: constraints?.asText
|
|
455
|
-
? defaultExpr("datetime('now')")
|
|
456
|
-
: defaultExpr("strftime('%s', 'now')"),
|
|
457
|
-
...constraints,
|
|
458
|
-
}),
|
|
459
|
-
|
|
460
|
-
/**
|
|
461
|
-
* Create an updated_at timestamp column
|
|
462
|
-
*/
|
|
463
|
-
updatedAt: (
|
|
464
|
-
constraints?: Omit<ColumnConstraints, "default"> & { asText?: boolean },
|
|
465
|
-
): ColumnDefinition => ({
|
|
466
|
-
type: constraints?.asText ? SQLiteTypes.DATETIME : SQLiteTypes.INTEGER,
|
|
467
|
-
default: constraints?.asText
|
|
468
|
-
? defaultExpr("datetime('now')")
|
|
469
|
-
: defaultExpr("strftime('%s', 'now')"),
|
|
470
|
-
...constraints,
|
|
471
|
-
}),
|
|
472
|
-
|
|
473
|
-
/**
|
|
474
|
-
* Create a foreign key reference column
|
|
475
|
-
*/
|
|
476
|
-
foreignKey: (
|
|
477
|
-
refTable: string,
|
|
478
|
-
refColumn = "id",
|
|
479
|
-
constraints?: ColumnConstraints & {
|
|
480
|
-
onDelete?: ForeignKeyAction;
|
|
481
|
-
onUpdate?: ForeignKeyAction;
|
|
482
|
-
type?: SQLiteType;
|
|
483
|
-
},
|
|
484
|
-
): ColumnDefinition => ({
|
|
485
|
-
type: constraints?.type || SQLiteTypes.INTEGER,
|
|
486
|
-
references: {
|
|
487
|
-
table: refTable,
|
|
488
|
-
column: refColumn,
|
|
489
|
-
onDelete: constraints?.onDelete,
|
|
490
|
-
onUpdate: constraints?.onUpdate,
|
|
491
|
-
},
|
|
492
|
-
...constraints,
|
|
493
|
-
}),
|
|
494
|
-
|
|
495
|
-
/**
|
|
496
|
-
* Create an enum column (with CHECK constraint)
|
|
497
|
-
*/
|
|
498
|
-
enum: (
|
|
499
|
-
values: string[],
|
|
500
|
-
constraints?: ColumnConstraints,
|
|
501
|
-
): ColumnDefinition => ({
|
|
502
|
-
type: SQLiteTypes.TEXT,
|
|
503
|
-
notNull: true,
|
|
504
|
-
check: `{{COLUMN}} IN (${values.map((v) => `'${v}'`).join(", ")})`,
|
|
505
|
-
...constraints,
|
|
506
|
-
}),
|
|
507
|
-
};
|
|
508
|
-
|
|
509
|
-
/**
|
|
510
|
-
* SQL function helpers for use in defaults and expressions
|
|
511
|
-
*/
|
|
512
|
-
export const sql = {
|
|
513
|
-
// Current date/time
|
|
514
|
-
now: () => defaultExpr("datetime('now')"),
|
|
515
|
-
currentTime: () => defaultExpr("time('now')"),
|
|
516
|
-
currentDate: () => defaultExpr("date('now')"),
|
|
517
|
-
currentTimestamp: () => defaultExpr("datetime('now')"),
|
|
518
|
-
unixTimestamp: () => defaultExpr("strftime('%s', 'now')"),
|
|
519
|
-
|
|
520
|
-
// Functions
|
|
521
|
-
...SQLiteFunctions,
|
|
522
|
-
|
|
523
|
-
// Raw expression
|
|
524
|
-
raw: (expression: string) => defaultExpr(expression),
|
|
525
|
-
|
|
526
|
-
// Literals
|
|
527
|
-
null: () => null,
|
|
528
|
-
true: () => 1,
|
|
529
|
-
false: () => 0,
|
|
530
|
-
};
|
|
531
|
-
|
|
532
|
-
/**
|
|
533
|
-
* Enhanced createTable method signature
|
|
534
|
-
*/
|
|
535
|
-
export type CreateTableColumns = string | Record<string, string> | TableSchema;
|
|
536
|
-
|
|
537
|
-
/**
|
|
538
|
-
* Query builder state interface
|
|
539
|
-
*/
|
|
540
|
-
export interface QueryBuilderState<T extends Record<string, unknown>> {
|
|
541
|
-
db: Database; // Database instance from bun:sqlite
|
|
542
|
-
tableName: string;
|
|
543
|
-
whereConditions: string[];
|
|
544
|
-
whereParams: SQLQueryBindings[];
|
|
545
|
-
regexConditions: Array<{
|
|
546
|
-
column: keyof T;
|
|
547
|
-
regex: RegExp;
|
|
548
|
-
}>;
|
|
549
|
-
jsonColumns?: Array<keyof T>;
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
/**
|
|
553
|
-
* Column names type for SELECT operations
|
|
554
|
-
*/
|
|
555
|
-
export type ColumnNames<T> = ["*"] | Array<keyof T>;
|
|
556
|
-
|
|
557
|
-
/**
|
|
558
|
-
* Order direction for ORDER BY clauses
|
|
559
|
-
*/
|
|
560
|
-
export type OrderDirection = "ASC" | "DESC";
|
|
561
|
-
|
|
562
|
-
/**
|
|
563
|
-
* WHERE condition object type
|
|
564
|
-
*/
|
|
565
|
-
export type WhereCondition<T> = Partial<T>;
|
|
566
|
-
|
|
567
|
-
/**
|
|
568
|
-
* Regex condition object type
|
|
569
|
-
*/
|
|
570
|
-
export type RegexCondition<T> = Partial<Record<keyof T, RegExp | string>>;
|
|
571
|
-
|
|
572
|
-
/**
|
|
573
|
-
* Insert result interface
|
|
574
|
-
*/
|
|
575
|
-
export interface InsertResult {
|
|
576
|
-
insertId: number;
|
|
577
|
-
changes: number;
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
/**
|
|
581
|
-
* Update result interface
|
|
582
|
-
*/
|
|
583
|
-
export interface UpdateResult {
|
|
584
|
-
changes: number;
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
/**
|
|
588
|
-
* Delete result interface
|
|
589
|
-
*/
|
|
590
|
-
export interface DeleteResult {
|
|
591
|
-
changes: number;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
/**
|
|
595
|
-
* Insert options interface
|
|
596
|
-
*/
|
|
597
|
-
export interface InsertOptions {
|
|
598
|
-
orIgnore?: boolean;
|
|
599
|
-
orReplace?: boolean;
|
|
600
|
-
orAbort?: boolean;
|
|
601
|
-
orFail?: boolean;
|
|
602
|
-
orRollback?: boolean;
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
/**
|
|
606
|
-
* JSON column configuration
|
|
607
|
-
*/
|
|
608
|
-
export type JsonColumnConfig<T> = Array<keyof T>
|
|
609
|
-
|
|
610
|
-
/**
|
|
611
|
-
* Generic database row type
|
|
612
|
-
*/
|
|
613
|
-
export type DatabaseRow = Record<string, unknown>;
|
|
614
|
-
|
|
615
|
-
/**
|
|
616
|
-
* SQL parameter type
|
|
617
|
-
*/
|
|
618
|
-
export type SqlParameter = SQLQueryBindings;
|
|
619
|
-
|
|
620
|
-
/**
|
|
621
|
-
* Database row with unknown structure
|
|
622
|
-
*/
|
|
623
|
-
export type DatabaseRowData = Record<string, SQLQueryBindings>;
|
|
1
|
+
import type { Database, SQLQueryBindings } from "bun:sqlite";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* All SQLite data types including affinity types
|
|
5
|
+
*/
|
|
6
|
+
export const SQLiteTypes = {
|
|
7
|
+
// Standard SQLite types
|
|
8
|
+
INTEGER: "INTEGER" as const,
|
|
9
|
+
TEXT: "TEXT" as const,
|
|
10
|
+
REAL: "REAL" as const,
|
|
11
|
+
BLOB: "BLOB" as const,
|
|
12
|
+
NUMERIC: "NUMERIC" as const,
|
|
13
|
+
|
|
14
|
+
// Common type aliases and variations
|
|
15
|
+
INT: "INT" as const,
|
|
16
|
+
TINYINT: "TINYINT" as const,
|
|
17
|
+
SMALLINT: "SMALLINT" as const,
|
|
18
|
+
MEDIUMINT: "MEDIUMINT" as const,
|
|
19
|
+
BIGINT: "BIGINT" as const,
|
|
20
|
+
|
|
21
|
+
// Text variations
|
|
22
|
+
VARCHAR: "VARCHAR" as const,
|
|
23
|
+
CHAR: "CHAR" as const,
|
|
24
|
+
CHARACTER: "CHARACTER" as const,
|
|
25
|
+
NCHAR: "NCHAR" as const,
|
|
26
|
+
NVARCHAR: "NVARCHAR" as const,
|
|
27
|
+
CLOB: "CLOB" as const,
|
|
28
|
+
|
|
29
|
+
// Numeric variations
|
|
30
|
+
DOUBLE: "DOUBLE" as const,
|
|
31
|
+
FLOAT: "FLOAT" as const,
|
|
32
|
+
DECIMAL: "DECIMAL" as const,
|
|
33
|
+
|
|
34
|
+
// Date/Time (stored as TEXT, INTEGER, or REAL)
|
|
35
|
+
DATE: "DATE" as const,
|
|
36
|
+
DATETIME: "DATETIME" as const,
|
|
37
|
+
TIMESTAMP: "TIMESTAMP" as const,
|
|
38
|
+
TIME: "TIME" as const,
|
|
39
|
+
|
|
40
|
+
// Boolean (stored as INTEGER)
|
|
41
|
+
BOOLEAN: "BOOLEAN" as const,
|
|
42
|
+
|
|
43
|
+
// JSON (stored as TEXT)
|
|
44
|
+
JSON: "JSON" as const,
|
|
45
|
+
} as const;
|
|
46
|
+
|
|
47
|
+
export type SQLiteType = (typeof SQLiteTypes)[keyof typeof SQLiteTypes];
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* SQLite built-in scalar functions
|
|
51
|
+
*/
|
|
52
|
+
export const SQLiteFunctions = {
|
|
53
|
+
// Date/Time functions
|
|
54
|
+
DATE: (expr: string) => `DATE(${expr})`,
|
|
55
|
+
TIME: (expr: string) => `TIME(${expr})`,
|
|
56
|
+
DATETIME: (expr: string) => `DATETIME(${expr})`,
|
|
57
|
+
JULIANDAY: (expr: string) => `JULIANDAY(${expr})`,
|
|
58
|
+
STRFTIME: (format: string, expr: string) => `STRFTIME('${format}', ${expr})`,
|
|
59
|
+
|
|
60
|
+
// String functions
|
|
61
|
+
LENGTH: (expr: string) => `LENGTH(${expr})`,
|
|
62
|
+
LOWER: (expr: string) => `LOWER(${expr})`,
|
|
63
|
+
UPPER: (expr: string) => `UPPER(${expr})`,
|
|
64
|
+
TRIM: (expr: string, chars?: string) =>
|
|
65
|
+
chars ? `TRIM(${expr}, '${chars}')` : `TRIM(${expr})`,
|
|
66
|
+
LTRIM: (expr: string, chars?: string) =>
|
|
67
|
+
chars ? `LTRIM(${expr}, '${chars}')` : `LTRIM(${expr})`,
|
|
68
|
+
RTRIM: (expr: string, chars?: string) =>
|
|
69
|
+
chars ? `RTRIM(${expr}, '${chars}')` : `RTRIM(${expr})`,
|
|
70
|
+
SUBSTR: (expr: string, start: number, length?: number) =>
|
|
71
|
+
length
|
|
72
|
+
? `SUBSTR(${expr}, ${start}, ${length})`
|
|
73
|
+
: `SUBSTR(${expr}, ${start})`,
|
|
74
|
+
SUBSTRING: (expr: string, start: number, length?: number) =>
|
|
75
|
+
length
|
|
76
|
+
? `SUBSTRING(${expr}, ${start}, ${length})`
|
|
77
|
+
: `SUBSTRING(${expr}, ${start})`,
|
|
78
|
+
REPLACE: (expr: string, old: string, replacement: string) =>
|
|
79
|
+
`REPLACE(${expr}, '${old}', '${replacement}')`,
|
|
80
|
+
PRINTF: (format: string, ...args: string[]) =>
|
|
81
|
+
`PRINTF('${format}', ${args.join(", ")})`,
|
|
82
|
+
|
|
83
|
+
// Math functions
|
|
84
|
+
ABS: (expr: string) => `ABS(${expr})`,
|
|
85
|
+
ROUND: (expr: string, digits?: number) =>
|
|
86
|
+
digits !== undefined ? `ROUND(${expr}, ${digits})` : `ROUND(${expr})`,
|
|
87
|
+
RANDOM: () => "RANDOM()",
|
|
88
|
+
MIN: (...exprs: string[]) => `MIN(${exprs.join(", ")})`,
|
|
89
|
+
MAX: (...exprs: string[]) => `MAX(${exprs.join(", ")})`,
|
|
90
|
+
|
|
91
|
+
// Type conversion
|
|
92
|
+
CAST: (expr: string, type: string) => `CAST(${expr} AS ${type})`,
|
|
93
|
+
TYPEOF: (expr: string) => `TYPEOF(${expr})`,
|
|
94
|
+
|
|
95
|
+
// Conditional
|
|
96
|
+
COALESCE: (...exprs: string[]) => `COALESCE(${exprs.join(", ")})`,
|
|
97
|
+
IFNULL: (expr: string, replacement: string) =>
|
|
98
|
+
`IFNULL(${expr}, ${replacement})`,
|
|
99
|
+
NULLIF: (expr1: string, expr2: string) => `NULLIF(${expr1}, ${expr2})`,
|
|
100
|
+
IIF: (condition: string, trueValue: string, falseValue: string) =>
|
|
101
|
+
`IIF(${condition}, ${trueValue}, ${falseValue})`,
|
|
102
|
+
|
|
103
|
+
// Aggregate functions (for use in expressions)
|
|
104
|
+
COUNT: (expr?: string) => (expr ? `COUNT(${expr})` : "COUNT(*)"),
|
|
105
|
+
SUM: (expr: string) => `SUM(${expr})`,
|
|
106
|
+
AVG: (expr: string) => `AVG(${expr})`,
|
|
107
|
+
TOTAL: (expr: string) => `TOTAL(${expr})`,
|
|
108
|
+
GROUP_CONCAT: (expr: string, separator?: string) =>
|
|
109
|
+
separator
|
|
110
|
+
? `GROUP_CONCAT(${expr}, '${separator}')`
|
|
111
|
+
: `GROUP_CONCAT(${expr})`,
|
|
112
|
+
|
|
113
|
+
// JSON functions (SQLite 3.45+)
|
|
114
|
+
JSON: (expr: string) => `JSON(${expr})`,
|
|
115
|
+
JSON_EXTRACT: (json: string, path: string) =>
|
|
116
|
+
`JSON_EXTRACT(${json}, '${path}')`,
|
|
117
|
+
JSON_TYPE: (json: string, path?: string) =>
|
|
118
|
+
path ? `JSON_TYPE(${json}, '${path}')` : `JSON_TYPE(${json})`,
|
|
119
|
+
JSON_VALID: (expr: string) => `JSON_VALID(${expr})`,
|
|
120
|
+
JSON_ARRAY: (...values: string[]) => `JSON_ARRAY(${values.join(", ")})`,
|
|
121
|
+
JSON_OBJECT: (...pairs: string[]) => `JSON_OBJECT(${pairs.join(", ")})`,
|
|
122
|
+
} as const;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* SQLite keywords and special values
|
|
126
|
+
*/
|
|
127
|
+
export const SQLiteKeywords = {
|
|
128
|
+
// Special values
|
|
129
|
+
NULL: "NULL" as const,
|
|
130
|
+
CURRENT_TIME: "CURRENT_TIME" as const,
|
|
131
|
+
CURRENT_DATE: "CURRENT_DATE" as const,
|
|
132
|
+
CURRENT_TIMESTAMP: "CURRENT_TIMESTAMP" as const,
|
|
133
|
+
|
|
134
|
+
// Boolean values (as integers)
|
|
135
|
+
TRUE: "1" as const,
|
|
136
|
+
FALSE: "0" as const,
|
|
137
|
+
|
|
138
|
+
// Conflict resolution
|
|
139
|
+
ROLLBACK: "ROLLBACK" as const,
|
|
140
|
+
ABORT: "ABORT" as const,
|
|
141
|
+
FAIL: "FAIL" as const,
|
|
142
|
+
IGNORE: "IGNORE" as const,
|
|
143
|
+
REPLACE: "REPLACE" as const,
|
|
144
|
+
} as const;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Foreign key actions
|
|
148
|
+
*/
|
|
149
|
+
export type ForeignKeyAction =
|
|
150
|
+
| "CASCADE"
|
|
151
|
+
| "SET NULL"
|
|
152
|
+
| "RESTRICT"
|
|
153
|
+
| "NO ACTION"
|
|
154
|
+
| "SET DEFAULT";
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Column constraint options with comprehensive support
|
|
158
|
+
*/
|
|
159
|
+
export interface ColumnConstraints {
|
|
160
|
+
/** Column is PRIMARY KEY */
|
|
161
|
+
primaryKey?: boolean;
|
|
162
|
+
/** Column has AUTOINCREMENT (only valid with INTEGER PRIMARY KEY) */
|
|
163
|
+
autoincrement?: boolean;
|
|
164
|
+
/** Column is NOT NULL */
|
|
165
|
+
notNull?: boolean;
|
|
166
|
+
/** Column has UNIQUE constraint */
|
|
167
|
+
unique?: boolean;
|
|
168
|
+
/** Default value - can be literal value, function call, or keyword */
|
|
169
|
+
default?: string | number | boolean | null | DefaultExpression;
|
|
170
|
+
/** Custom check constraint */
|
|
171
|
+
check?: string;
|
|
172
|
+
/** Collation sequence */
|
|
173
|
+
collate?: "BINARY" | "NOCASE" | "RTRIM" | string;
|
|
174
|
+
/** References another table (foreign key) */
|
|
175
|
+
references?: {
|
|
176
|
+
table: string;
|
|
177
|
+
column: string;
|
|
178
|
+
onDelete?: ForeignKeyAction;
|
|
179
|
+
onUpdate?: ForeignKeyAction;
|
|
180
|
+
};
|
|
181
|
+
/** Generated column expression (GENERATED ALWAYS AS) */
|
|
182
|
+
generated?: {
|
|
183
|
+
expression: string;
|
|
184
|
+
stored?: boolean; // true for STORED, false/undefined for VIRTUAL
|
|
185
|
+
};
|
|
186
|
+
/** Column comment (stored as metadata, not in schema) */
|
|
187
|
+
comment?: string;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Type-safe default value expressions
|
|
192
|
+
*/
|
|
193
|
+
export type DefaultExpression = {
|
|
194
|
+
_type: "expression";
|
|
195
|
+
expression: string;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Helper to create default expressions
|
|
200
|
+
*/
|
|
201
|
+
export const defaultExpr = (expression: string): DefaultExpression => ({
|
|
202
|
+
_type: "expression",
|
|
203
|
+
expression,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Type-safe column definition
|
|
208
|
+
*/
|
|
209
|
+
export interface ColumnDefinition extends ColumnConstraints {
|
|
210
|
+
/** SQLite data type */
|
|
211
|
+
type: SQLiteType;
|
|
212
|
+
/** Optional type parameters (e.g., VARCHAR(255)) */
|
|
213
|
+
length?: number;
|
|
214
|
+
/** Precision for DECIMAL/NUMERIC types */
|
|
215
|
+
precision?: number;
|
|
216
|
+
/** Scale for DECIMAL/NUMERIC types */
|
|
217
|
+
scale?: number;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Type-safe table schema definition
|
|
222
|
+
*/
|
|
223
|
+
export type TableSchema = Record<string, ColumnDefinition>;
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
export type TypedTableSchema<T extends string = string> = Record<T, ColumnDefinition>;
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Table constraint types
|
|
230
|
+
*/
|
|
231
|
+
export interface TableConstraints {
|
|
232
|
+
/** PRIMARY KEY constraint on multiple columns */
|
|
233
|
+
primaryKey?: string[];
|
|
234
|
+
/** UNIQUE constraints */
|
|
235
|
+
unique?: string[] | string[][];
|
|
236
|
+
/** CHECK constraints */
|
|
237
|
+
check?: string[];
|
|
238
|
+
/** FOREIGN KEY constraints */
|
|
239
|
+
foreignKeys?: Array<{
|
|
240
|
+
columns: string[];
|
|
241
|
+
references: {
|
|
242
|
+
table: string;
|
|
243
|
+
columns: string[];
|
|
244
|
+
onDelete?: ForeignKeyAction;
|
|
245
|
+
onUpdate?: ForeignKeyAction;
|
|
246
|
+
};
|
|
247
|
+
}>;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Enhanced table options
|
|
252
|
+
*/
|
|
253
|
+
export interface TableOptions<T> {
|
|
254
|
+
/** Add IF NOT EXISTS clause */
|
|
255
|
+
ifNotExists?: boolean;
|
|
256
|
+
/** Create WITHOUT ROWID table */
|
|
257
|
+
withoutRowId?: boolean;
|
|
258
|
+
/** Table constraints */
|
|
259
|
+
constraints?: TableConstraints;
|
|
260
|
+
/** Temporary table */
|
|
261
|
+
temporary?: boolean;
|
|
262
|
+
/** Table comment */
|
|
263
|
+
comment?: string;
|
|
264
|
+
|
|
265
|
+
jsonConfig?: JsonColumnConfig<T>
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Comprehensive column helper functions with full type support
|
|
270
|
+
*/
|
|
271
|
+
export const column = {
|
|
272
|
+
/**
|
|
273
|
+
* Create an INTEGER column with optional size specification
|
|
274
|
+
*/
|
|
275
|
+
integer: (
|
|
276
|
+
constraints?: ColumnConstraints & {
|
|
277
|
+
size?: "TINYINT" | "SMALLINT" | "MEDIUMINT" | "BIGINT";
|
|
278
|
+
},
|
|
279
|
+
): ColumnDefinition => ({
|
|
280
|
+
type: constraints?.size || SQLiteTypes.INTEGER,
|
|
281
|
+
...constraints,
|
|
282
|
+
}),
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Create a TEXT column with optional length
|
|
286
|
+
*/
|
|
287
|
+
text: (
|
|
288
|
+
constraints?: ColumnConstraints & {
|
|
289
|
+
length?: number;
|
|
290
|
+
variant?: "VARCHAR" | "CHAR" | "CLOB" | "NCHAR" | "NVARCHAR";
|
|
291
|
+
},
|
|
292
|
+
): ColumnDefinition => ({
|
|
293
|
+
type: constraints?.variant || SQLiteTypes.TEXT,
|
|
294
|
+
length: constraints?.length,
|
|
295
|
+
...constraints,
|
|
296
|
+
}),
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Create a REAL column
|
|
300
|
+
*/
|
|
301
|
+
real: (
|
|
302
|
+
constraints?: ColumnConstraints & { variant?: "DOUBLE" | "FLOAT" },
|
|
303
|
+
): ColumnDefinition => ({
|
|
304
|
+
type: constraints?.variant || SQLiteTypes.REAL,
|
|
305
|
+
...constraints,
|
|
306
|
+
}),
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Create a BLOB column
|
|
310
|
+
*/
|
|
311
|
+
blob: (constraints?: ColumnConstraints): ColumnDefinition => ({
|
|
312
|
+
type: SQLiteTypes.BLOB,
|
|
313
|
+
...constraints,
|
|
314
|
+
}),
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Create a NUMERIC/DECIMAL column with precision and scale
|
|
318
|
+
*/
|
|
319
|
+
numeric: (
|
|
320
|
+
constraints?: ColumnConstraints & {
|
|
321
|
+
precision?: number;
|
|
322
|
+
scale?: number;
|
|
323
|
+
variant?: "DECIMAL";
|
|
324
|
+
},
|
|
325
|
+
): ColumnDefinition => ({
|
|
326
|
+
type: constraints?.variant || SQLiteTypes.NUMERIC,
|
|
327
|
+
precision: constraints?.precision,
|
|
328
|
+
scale: constraints?.scale,
|
|
329
|
+
...constraints,
|
|
330
|
+
}),
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Create a DATE column (stored as TEXT)
|
|
334
|
+
*/
|
|
335
|
+
date: (constraints?: ColumnConstraints): ColumnDefinition => ({
|
|
336
|
+
type: SQLiteTypes.DATE,
|
|
337
|
+
...constraints,
|
|
338
|
+
}),
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Create a DATETIME column (stored as TEXT)
|
|
342
|
+
*/
|
|
343
|
+
datetime: (constraints?: ColumnConstraints): ColumnDefinition => ({
|
|
344
|
+
type: SQLiteTypes.DATETIME,
|
|
345
|
+
...constraints,
|
|
346
|
+
}),
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Create a TIMESTAMP column (stored as INTEGER by default)
|
|
350
|
+
*/
|
|
351
|
+
timestamp: (
|
|
352
|
+
constraints?: ColumnConstraints & { asText?: boolean },
|
|
353
|
+
): ColumnDefinition => ({
|
|
354
|
+
type: constraints?.asText ? SQLiteTypes.TEXT : SQLiteTypes.INTEGER,
|
|
355
|
+
...constraints,
|
|
356
|
+
}),
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Create a TIME column (stored as TEXT)
|
|
360
|
+
*/
|
|
361
|
+
time: (constraints?: ColumnConstraints): ColumnDefinition => ({
|
|
362
|
+
type: SQLiteTypes.TIME,
|
|
363
|
+
...constraints,
|
|
364
|
+
}),
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Create a BOOLEAN column (stored as INTEGER)
|
|
368
|
+
*/
|
|
369
|
+
boolean: (constraints?: ColumnConstraints): ColumnDefinition => ({
|
|
370
|
+
type: SQLiteTypes.BOOLEAN,
|
|
371
|
+
check: constraints?.check || "{{COLUMN}} IN (0, 1)", // Placeholder for column name
|
|
372
|
+
...constraints,
|
|
373
|
+
}),
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Create a JSON column (stored as TEXT)
|
|
377
|
+
*/
|
|
378
|
+
json: (
|
|
379
|
+
constraints: ColumnConstraints & { validateJson?: boolean },
|
|
380
|
+
): ColumnDefinition => ({
|
|
381
|
+
type: SQLiteTypes.JSON,
|
|
382
|
+
check: constraints?.validateJson
|
|
383
|
+
? "JSON_VALID({{COLUMN}})"
|
|
384
|
+
: constraints?.check,
|
|
385
|
+
...constraints,
|
|
386
|
+
}),
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Create a VARCHAR column with specified length
|
|
390
|
+
*/
|
|
391
|
+
varchar: (
|
|
392
|
+
length: number,
|
|
393
|
+
constraints?: ColumnConstraints,
|
|
394
|
+
): ColumnDefinition => ({
|
|
395
|
+
type: SQLiteTypes.VARCHAR,
|
|
396
|
+
length,
|
|
397
|
+
...constraints,
|
|
398
|
+
}),
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Create a CHAR column with specified length
|
|
402
|
+
*/
|
|
403
|
+
char: (
|
|
404
|
+
length: number,
|
|
405
|
+
constraints?: ColumnConstraints,
|
|
406
|
+
): ColumnDefinition => ({
|
|
407
|
+
type: SQLiteTypes.CHAR,
|
|
408
|
+
length,
|
|
409
|
+
...constraints,
|
|
410
|
+
}),
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Create an auto-incrementing primary key column
|
|
414
|
+
*/
|
|
415
|
+
id: (
|
|
416
|
+
constraints?: Omit<
|
|
417
|
+
ColumnConstraints,
|
|
418
|
+
"primaryKey" | "autoincrement" | "notNull"
|
|
419
|
+
>,
|
|
420
|
+
): ColumnDefinition => ({
|
|
421
|
+
type: SQLiteTypes.INTEGER,
|
|
422
|
+
primaryKey: true,
|
|
423
|
+
autoincrement: true,
|
|
424
|
+
notNull: true,
|
|
425
|
+
...constraints,
|
|
426
|
+
}),
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Create a UUID column (stored as TEXT)
|
|
430
|
+
*/
|
|
431
|
+
uuid: (
|
|
432
|
+
constraints?: ColumnConstraints & { generateDefault?: boolean },
|
|
433
|
+
): ColumnDefinition => ({
|
|
434
|
+
type: SQLiteTypes.TEXT,
|
|
435
|
+
length: 36,
|
|
436
|
+
default: constraints?.generateDefault
|
|
437
|
+
? defaultExpr(
|
|
438
|
+
"lower(hex(randomblob(4))) || '-' || lower(hex(randomblob(2))) || '-4' || substr(lower(hex(randomblob(2))),2) || '-' || substr('89ab',abs(random()) % 4 + 1, 1) || substr(lower(hex(randomblob(2))),2) || '-' || lower(hex(randomblob(6)))",
|
|
439
|
+
)
|
|
440
|
+
: constraints?.default,
|
|
441
|
+
...constraints,
|
|
442
|
+
}),
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Create a created_at timestamp column
|
|
446
|
+
*/
|
|
447
|
+
createdAt: (
|
|
448
|
+
constraints?: Omit<ColumnConstraints, "default" | "notNull"> & {
|
|
449
|
+
asText?: boolean;
|
|
450
|
+
},
|
|
451
|
+
): ColumnDefinition => ({
|
|
452
|
+
type: constraints?.asText ? SQLiteTypes.DATETIME : SQLiteTypes.INTEGER,
|
|
453
|
+
notNull: true,
|
|
454
|
+
default: constraints?.asText
|
|
455
|
+
? defaultExpr("datetime('now')")
|
|
456
|
+
: defaultExpr("strftime('%s', 'now')"),
|
|
457
|
+
...constraints,
|
|
458
|
+
}),
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Create an updated_at timestamp column
|
|
462
|
+
*/
|
|
463
|
+
updatedAt: (
|
|
464
|
+
constraints?: Omit<ColumnConstraints, "default"> & { asText?: boolean },
|
|
465
|
+
): ColumnDefinition => ({
|
|
466
|
+
type: constraints?.asText ? SQLiteTypes.DATETIME : SQLiteTypes.INTEGER,
|
|
467
|
+
default: constraints?.asText
|
|
468
|
+
? defaultExpr("datetime('now')")
|
|
469
|
+
: defaultExpr("strftime('%s', 'now')"),
|
|
470
|
+
...constraints,
|
|
471
|
+
}),
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Create a foreign key reference column
|
|
475
|
+
*/
|
|
476
|
+
foreignKey: (
|
|
477
|
+
refTable: string,
|
|
478
|
+
refColumn = "id",
|
|
479
|
+
constraints?: ColumnConstraints & {
|
|
480
|
+
onDelete?: ForeignKeyAction;
|
|
481
|
+
onUpdate?: ForeignKeyAction;
|
|
482
|
+
type?: SQLiteType;
|
|
483
|
+
},
|
|
484
|
+
): ColumnDefinition => ({
|
|
485
|
+
type: constraints?.type || SQLiteTypes.INTEGER,
|
|
486
|
+
references: {
|
|
487
|
+
table: refTable,
|
|
488
|
+
column: refColumn,
|
|
489
|
+
onDelete: constraints?.onDelete,
|
|
490
|
+
onUpdate: constraints?.onUpdate,
|
|
491
|
+
},
|
|
492
|
+
...constraints,
|
|
493
|
+
}),
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Create an enum column (with CHECK constraint)
|
|
497
|
+
*/
|
|
498
|
+
enum: (
|
|
499
|
+
values: string[],
|
|
500
|
+
constraints?: ColumnConstraints,
|
|
501
|
+
): ColumnDefinition => ({
|
|
502
|
+
type: SQLiteTypes.TEXT,
|
|
503
|
+
notNull: true,
|
|
504
|
+
check: `{{COLUMN}} IN (${values.map((v) => `'${v}'`).join(", ")})`,
|
|
505
|
+
...constraints,
|
|
506
|
+
}),
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* SQL function helpers for use in defaults and expressions
|
|
511
|
+
*/
|
|
512
|
+
export const sql = {
|
|
513
|
+
// Current date/time
|
|
514
|
+
now: () => defaultExpr("datetime('now')"),
|
|
515
|
+
currentTime: () => defaultExpr("time('now')"),
|
|
516
|
+
currentDate: () => defaultExpr("date('now')"),
|
|
517
|
+
currentTimestamp: () => defaultExpr("datetime('now')"),
|
|
518
|
+
unixTimestamp: () => defaultExpr("strftime('%s', 'now')"),
|
|
519
|
+
|
|
520
|
+
// Functions
|
|
521
|
+
...SQLiteFunctions,
|
|
522
|
+
|
|
523
|
+
// Raw expression
|
|
524
|
+
raw: (expression: string) => defaultExpr(expression),
|
|
525
|
+
|
|
526
|
+
// Literals
|
|
527
|
+
null: () => null,
|
|
528
|
+
true: () => 1,
|
|
529
|
+
false: () => 0,
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Enhanced createTable method signature
|
|
534
|
+
*/
|
|
535
|
+
export type CreateTableColumns = string | Record<string, string> | TableSchema;
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Query builder state interface
|
|
539
|
+
*/
|
|
540
|
+
export interface QueryBuilderState<T extends Record<string, unknown>> {
|
|
541
|
+
db: Database; // Database instance from bun:sqlite
|
|
542
|
+
tableName: string;
|
|
543
|
+
whereConditions: string[];
|
|
544
|
+
whereParams: SQLQueryBindings[];
|
|
545
|
+
regexConditions: Array<{
|
|
546
|
+
column: keyof T;
|
|
547
|
+
regex: RegExp;
|
|
548
|
+
}>;
|
|
549
|
+
jsonColumns?: Array<keyof T>;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Column names type for SELECT operations
|
|
554
|
+
*/
|
|
555
|
+
export type ColumnNames<T> = ["*"] | Array<keyof T>;
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Order direction for ORDER BY clauses
|
|
559
|
+
*/
|
|
560
|
+
export type OrderDirection = "ASC" | "DESC";
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* WHERE condition object type
|
|
564
|
+
*/
|
|
565
|
+
export type WhereCondition<T> = Partial<T>;
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Regex condition object type
|
|
569
|
+
*/
|
|
570
|
+
export type RegexCondition<T> = Partial<Record<keyof T, RegExp | string>>;
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Insert result interface
|
|
574
|
+
*/
|
|
575
|
+
export interface InsertResult {
|
|
576
|
+
insertId: number;
|
|
577
|
+
changes: number;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Update result interface
|
|
582
|
+
*/
|
|
583
|
+
export interface UpdateResult {
|
|
584
|
+
changes: number;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Delete result interface
|
|
589
|
+
*/
|
|
590
|
+
export interface DeleteResult {
|
|
591
|
+
changes: number;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Insert options interface
|
|
596
|
+
*/
|
|
597
|
+
export interface InsertOptions {
|
|
598
|
+
orIgnore?: boolean;
|
|
599
|
+
orReplace?: boolean;
|
|
600
|
+
orAbort?: boolean;
|
|
601
|
+
orFail?: boolean;
|
|
602
|
+
orRollback?: boolean;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* JSON column configuration
|
|
607
|
+
*/
|
|
608
|
+
export type JsonColumnConfig<T> = Array<keyof T>
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Generic database row type
|
|
612
|
+
*/
|
|
613
|
+
export type DatabaseRow = Record<string, unknown>;
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* SQL parameter type
|
|
617
|
+
*/
|
|
618
|
+
export type SqlParameter = SQLQueryBindings;
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Database row with unknown structure
|
|
622
|
+
*/
|
|
623
|
+
export type DatabaseRowData = Record<string, SQLQueryBindings>;
|