@dockstat/sqlite-wrapper 1.2.8 → 1.3.1

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