@dockstat/sqlite-wrapper 1.2.7 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,19 +1,19 @@
1
- import type { Database, SQLQueryBindings } from "bun:sqlite";
1
+ import type { Database, SQLQueryBindings } from "bun:sqlite"
2
2
  import type {
3
3
  ColumnNames,
4
- WhereCondition,
5
- RegexCondition,
6
- InsertResult,
7
- UpdateResult,
8
4
  DeleteResult,
9
5
  InsertOptions,
10
- JsonColumnConfig,
6
+ InsertResult,
7
+ Parser,
11
8
  QueryBuilderState,
12
- } from "../types";
13
- import { SelectQueryBuilder } from "./select";
14
- import { InsertQueryBuilder } from "./insert";
15
- import { UpdateQueryBuilder } from "./update";
16
- import { DeleteQueryBuilder } from "./delete";
9
+ RegexCondition,
10
+ UpdateResult,
11
+ WhereCondition,
12
+ } from "../types"
13
+ import { DeleteQueryBuilder } from "./delete"
14
+ import { InsertQueryBuilder } from "./insert"
15
+ import { SelectQueryBuilder } from "./select"
16
+ import { UpdateQueryBuilder } from "./update"
17
17
 
18
18
  /**
19
19
  * Main QueryBuilder class that combines all functionality using composition.
@@ -26,40 +26,31 @@ import { DeleteQueryBuilder } from "./delete";
26
26
  * - DELETE: safe deletes with mandatory WHERE conditions
27
27
  * - WHERE: shared conditional logic across all operations
28
28
  */
29
- export class QueryBuilder<T extends Record<string, unknown>> {
30
- private selectBuilder: SelectQueryBuilder<T>;
31
- private insertBuilder: InsertQueryBuilder<T>;
32
- private updateBuilder: UpdateQueryBuilder<T>;
33
- private deleteBuilder: DeleteQueryBuilder<T>;
34
-
35
- constructor(
36
- db: Database,
37
- tableName: string,
38
- jsonConfig?: JsonColumnConfig<T>,
39
- ) {
29
+ export class QueryBuilder<T extends Record<string, unknown> = Record<string, unknown>> {
30
+ private selectBuilder: SelectQueryBuilder<T>
31
+ private insertBuilder: InsertQueryBuilder<T>
32
+ private updateBuilder: UpdateQueryBuilder<T>
33
+ private deleteBuilder: DeleteQueryBuilder<T>
34
+
35
+ constructor(db: Database, tableName: string, parser: Parser<T>) {
40
36
  // Create instances of each specialized builder
41
- this.selectBuilder = new SelectQueryBuilder<T>(db, tableName, jsonConfig);
42
- this.insertBuilder = new InsertQueryBuilder<T>(db, tableName, jsonConfig);
43
- this.updateBuilder = new UpdateQueryBuilder<T>(db, tableName, jsonConfig);
44
- this.deleteBuilder = new DeleteQueryBuilder<T>(db, tableName, jsonConfig);
37
+ this.selectBuilder = new SelectQueryBuilder<T>(db, tableName, parser)
38
+ this.insertBuilder = new InsertQueryBuilder<T>(db, tableName, parser)
39
+ this.updateBuilder = new UpdateQueryBuilder<T>(db, tableName, parser)
40
+ this.deleteBuilder = new DeleteQueryBuilder<T>(db, tableName, parser)
45
41
 
46
42
  // Ensure all builders share the same state for WHERE conditions
47
- this.syncBuilderStates();
43
+ this.syncBuilderStates()
48
44
  }
49
45
 
50
46
  /**
51
47
  * Synchronize the state between all builders so WHERE conditions are shared.
52
48
  */
53
49
  private syncBuilderStates(): void {
54
- const masterState = (
55
- this.selectBuilder as unknown as { state: QueryBuilderState<T> }
56
- ).state;
57
- (this.insertBuilder as unknown as { state: QueryBuilderState<T> }).state =
58
- masterState;
59
- (this.updateBuilder as unknown as { state: QueryBuilderState<T> }).state =
60
- masterState;
61
- (this.deleteBuilder as unknown as { state: QueryBuilderState<T> }).state =
62
- masterState;
50
+ const masterState = (this.selectBuilder as unknown as { state: QueryBuilderState<T> }).state
51
+ ;(this.insertBuilder as unknown as { state: QueryBuilderState<T> }).state = masterState
52
+ ;(this.updateBuilder as unknown as { state: QueryBuilderState<T> }).state = masterState
53
+ ;(this.deleteBuilder as unknown as { state: QueryBuilderState<T> }).state = masterState
63
54
  }
64
55
 
65
56
  // ===== WHERE METHODS (delegated to selectBuilder) =====
@@ -68,96 +59,88 @@ export class QueryBuilder<T extends Record<string, unknown>> {
68
59
  * Add simple equality conditions to the WHERE clause.
69
60
  */
70
61
  where(conditions: WhereCondition<T>): this {
71
- this.selectBuilder.where(conditions);
72
- return this;
62
+ this.selectBuilder.where(conditions)
63
+ return this
73
64
  }
74
65
 
75
66
  /**
76
67
  * Add regex conditions (applied client-side).
77
68
  */
78
69
  whereRgx(conditions: RegexCondition<T>): this {
79
- this.selectBuilder.whereRgx(conditions);
80
- return this;
70
+ this.selectBuilder.whereRgx(conditions)
71
+ return this
81
72
  }
82
73
 
83
74
  /**
84
75
  * Add a raw SQL WHERE fragment with parameter binding.
85
76
  */
86
77
  whereExpr(expr: string, params: SQLQueryBindings[] = []): this {
87
- this.selectBuilder.whereExpr(expr, params);
88
- return this;
78
+ this.selectBuilder.whereExpr(expr, params)
79
+ return this
89
80
  }
90
81
 
91
82
  /**
92
83
  * Alias for whereExpr.
93
84
  */
94
85
  whereRaw(expr: string, params: SQLQueryBindings[] = []): this {
95
- this.selectBuilder.whereRaw(expr, params);
96
- return this;
86
+ this.selectBuilder.whereRaw(expr, params)
87
+ return this
97
88
  }
98
89
 
99
90
  /**
100
91
  * Add an IN clause with proper parameter binding.
101
92
  */
102
93
  whereIn(column: keyof T, values: SQLQueryBindings[]): this {
103
- this.selectBuilder.whereIn(column, values);
104
- return this;
94
+ this.selectBuilder.whereIn(column, values)
95
+ return this
105
96
  }
106
97
 
107
98
  /**
108
99
  * Add a NOT IN clause with proper parameter binding.
109
100
  */
110
101
  whereNotIn(column: keyof T, values: SQLQueryBindings[]): this {
111
- this.selectBuilder.whereNotIn(column, values);
112
- return this;
102
+ this.selectBuilder.whereNotIn(column, values)
103
+ return this
113
104
  }
114
105
 
115
106
  /**
116
107
  * Add a comparison operator condition.
117
108
  */
118
109
  whereOp(column: keyof T, op: string, value: SQLQueryBindings): this {
119
- this.selectBuilder.whereOp(column, op, value);
120
- return this;
110
+ this.selectBuilder.whereOp(column, op, value)
111
+ return this
121
112
  }
122
113
 
123
114
  /**
124
115
  * Add a BETWEEN condition.
125
116
  */
126
- whereBetween(
127
- column: keyof T,
128
- min: SQLQueryBindings,
129
- max: SQLQueryBindings,
130
- ): this {
131
- this.selectBuilder.whereBetween(column, min, max);
132
- return this;
117
+ whereBetween(column: keyof T, min: SQLQueryBindings, max: SQLQueryBindings): this {
118
+ this.selectBuilder.whereBetween(column, min, max)
119
+ return this
133
120
  }
134
121
 
135
122
  /**
136
123
  * Add a NOT BETWEEN condition.
137
124
  */
138
- whereNotBetween(
139
- column: keyof T,
140
- min: SQLQueryBindings,
141
- max: SQLQueryBindings,
142
- ): this {
143
- this.selectBuilder.whereNotBetween(column, min, max);
144
- return this;
125
+ whereNotBetween(column: keyof T, min: SQLQueryBindings, max: SQLQueryBindings): this {
126
+ this.selectBuilder.whereNotBetween(column, min, max)
127
+ return this
145
128
  }
146
129
 
147
130
  /**
148
131
  * Add an IS NULL condition.
149
132
  */
150
133
  whereNull(column: keyof T): this {
151
- this.selectBuilder.whereNull(column);
152
- return this;
134
+ this.selectBuilder.whereNull(column)
135
+ return this
153
136
  }
154
137
 
155
138
  /**
156
139
  * Add an IS NOT NULL condition.
157
140
  */
158
141
  whereNotNull(column: keyof T): this {
159
- this.selectBuilder.whereNotNull(column);
160
- return this;
142
+ this.selectBuilder.whereNotNull(column)
143
+ return this
161
144
  }
162
145
 
163
146
  // ===== SELECT METHODS (delegated to selectBuilder) =====
@@ -166,48 +149,48 @@ export class QueryBuilder<T extends Record<string, unknown>> {
166
149
  * Specify which columns to select.
167
150
  */
168
151
  select(columns: ColumnNames<T>): this {
169
- this.selectBuilder.select(columns);
170
- return this;
152
+ this.selectBuilder.select(columns)
153
+ return this
171
154
  }
172
155
 
173
156
  /**
174
157
  * Add ORDER BY clause.
175
158
  */
176
159
  orderBy(column: keyof T): this {
177
- this.selectBuilder.orderBy(column);
178
- return this;
160
+ this.selectBuilder.orderBy(column)
161
+ return this
179
162
  }
180
163
 
181
164
  /**
182
165
  * Set order direction to descending.
183
166
  */
184
167
  desc(): this {
185
- this.selectBuilder.desc();
186
- return this;
168
+ this.selectBuilder.desc()
169
+ return this
187
170
  }
188
171
 
189
172
  /**
190
173
  * Set order direction to ascending.
191
174
  */
192
175
  asc(): this {
193
- this.selectBuilder.asc();
194
- return this;
176
+ this.selectBuilder.asc()
177
+ return this
195
178
  }
196
179
 
197
180
  /**
198
181
  * Add LIMIT clause.
199
182
  */
200
183
  limit(amount: number): this {
201
- this.selectBuilder.limit(amount);
202
- return this;
184
+ this.selectBuilder.limit(amount)
185
+ return this
203
186
  }
204
187
 
205
188
  /**
206
189
  * Add OFFSET clause.
207
190
  */
208
191
  offset(start: number): this {
209
- this.selectBuilder.offset(start);
210
- return this;
192
+ this.selectBuilder.offset(start)
193
+ return this
211
194
  }
212
195
 
213
196
  // ===== SELECT EXECUTION METHODS (delegated to selectBuilder) =====
@@ -216,49 +199,49 @@ export class QueryBuilder<T extends Record<string, unknown>> {
216
199
  * Execute the query and return all matching rows.
217
200
  */
218
201
  all(): T[] {
219
- return this.selectBuilder.all();
202
+ return this.selectBuilder.all()
220
203
  }
221
204
 
222
205
  /**
223
206
  * Execute the query and return the first matching row, or null.
224
207
  */
225
208
  get(): T | null {
226
- return this.selectBuilder.get();
209
+ return this.selectBuilder.get()
227
210
  }
228
211
 
229
212
  /**
230
213
  * Execute the query and return the first matching row, or null.
231
214
  */
232
215
  first(): T | null {
233
- return this.selectBuilder.first();
216
+ return this.selectBuilder.first()
234
217
  }
235
218
 
236
219
  /**
237
220
  * Execute a COUNT query and return the number of matching rows.
238
221
  */
239
222
  count(): number {
240
- return this.selectBuilder.count();
223
+ return this.selectBuilder.count()
241
224
  }
242
225
 
243
226
  /**
244
227
  * Check if any rows match the current conditions.
245
228
  */
246
229
  exists(): boolean {
247
- return this.selectBuilder.exists();
230
+ return this.selectBuilder.exists()
248
231
  }
249
232
 
250
233
  /**
251
234
  * Execute the query and return a single column value from the first row.
252
235
  */
253
236
  value<K extends keyof T>(column: K): T[K] | null {
254
- return this.selectBuilder.value(column);
237
+ return this.selectBuilder.value(column)
255
238
  }
256
239
 
257
240
  /**
258
241
  * Execute the query and return an array of values from a single column.
259
242
  */
260
243
  pluck<K extends keyof T>(column: K): T[K][] {
261
- return this.selectBuilder.pluck(column);
244
+ return this.selectBuilder.pluck(column)
262
245
  }
263
246
 
264
247
  // ===== INSERT METHODS (delegated to insertBuilder) =====
@@ -266,60 +249,57 @@ export class QueryBuilder<T extends Record<string, unknown>> {
266
249
  /**
267
250
  * Insert a single row or multiple rows into the table.
268
251
  */
269
- insert(
270
- data: Partial<T> | Partial<T>[],
271
- options?: InsertOptions,
272
- ): InsertResult {
273
- return this.insertBuilder.insert(data, options);
252
+ insert(data: Partial<T> | Partial<T>[], options?: InsertOptions): InsertResult {
253
+ return this.insertBuilder.insert(data, options)
274
254
  }
275
255
 
276
256
  /**
277
257
  * Insert with OR IGNORE conflict resolution.
278
258
  */
279
259
  insertOrIgnore(data: Partial<T> | Partial<T>[]): InsertResult {
280
- return this.insertBuilder.insertOrIgnore(data);
260
+ return this.insertBuilder.insertOrIgnore(data)
281
261
  }
282
262
 
283
263
  /**
284
264
  * Insert with OR REPLACE conflict resolution.
285
265
  */
286
266
  insertOrReplace(data: Partial<T> | Partial<T>[]): InsertResult {
287
- return this.insertBuilder.insertOrReplace(data);
267
+ return this.insertBuilder.insertOrReplace(data)
288
268
  }
289
269
 
290
270
  /**
291
271
  * Insert with OR ABORT conflict resolution.
292
272
  */
293
273
  insertOrAbort(data: Partial<T> | Partial<T>[]): InsertResult {
294
- return this.insertBuilder.insertOrAbort(data);
274
+ return this.insertBuilder.insertOrAbort(data)
295
275
  }
296
276
 
297
277
  /**
298
278
  * Insert with OR FAIL conflict resolution.
299
279
  */
300
280
  insertOrFail(data: Partial<T> | Partial<T>[]): InsertResult {
301
- return this.insertBuilder.insertOrFail(data);
281
+ return this.insertBuilder.insertOrFail(data)
302
282
  }
303
283
 
304
284
  /**
305
285
  * Insert with OR ROLLBACK conflict resolution.
306
286
  */
307
287
  insertOrRollback(data: Partial<T> | Partial<T>[]): InsertResult {
308
- return this.insertBuilder.insertOrRollback(data);
288
+ return this.insertBuilder.insertOrRollback(data)
309
289
  }
310
290
 
311
291
  /**
312
292
  * Insert and get the inserted row back.
313
293
  */
314
294
  insertAndGet(data: Partial<T>, options?: InsertOptions): T | null {
315
- return this.insertBuilder.insertAndGet(data, options);
295
+ return this.insertBuilder.insertAndGet(data, options)
316
296
  }
317
297
 
318
298
  /**
319
299
  * Batch insert with transaction support.
320
300
  */
321
301
  insertBatch(rows: Partial<T>[], options?: InsertOptions): InsertResult {
322
- return this.insertBuilder.insertBatch(rows, options);
302
+ return this.insertBuilder.insertBatch(rows, options)
323
303
  }
324
304
 
325
305
  // ===== UPDATE METHODS (delegated to updateBuilder) =====
@@ -328,44 +308,42 @@ export class QueryBuilder<T extends Record<string, unknown>> {
328
308
  * Update rows matching the WHERE conditions.
329
309
  */
330
310
  update(data: Partial<T>): UpdateResult {
331
- return this.updateBuilder.update(data);
311
+ return this.updateBuilder.update(data)
332
312
  }
333
313
 
334
314
  /**
335
315
  * Update or insert (upsert) using INSERT OR REPLACE.
336
316
  */
337
317
  upsert(data: Partial<T>): UpdateResult {
338
- return this.updateBuilder.upsert(data);
318
+ return this.updateBuilder.upsert(data)
339
319
  }
340
320
 
341
321
  /**
342
322
  * Increment a numeric column by a specified amount.
343
323
  */
344
324
  increment(column: keyof T, amount = 1): UpdateResult {
345
- return this.updateBuilder.increment(column, amount);
325
+ return this.updateBuilder.increment(column, amount)
346
326
  }
347
327
 
348
328
  /**
349
329
  * Decrement a numeric column by a specified amount.
350
330
  */
351
331
  decrement(column: keyof T, amount = 1): UpdateResult {
352
- return this.updateBuilder.decrement(column, amount);
332
+ return this.updateBuilder.decrement(column, amount)
353
333
  }
354
334
 
355
335
  /**
356
336
  * Update and get the updated rows back.
357
337
  */
358
338
  updateAndGet(data: Partial<T>): T[] {
359
- return this.updateBuilder.updateAndGet(data);
339
+ return this.updateBuilder.updateAndGet(data)
360
340
  }
361
341
 
362
342
  /**
363
343
  * Batch update multiple rows with different values.
364
344
  */
365
- updateBatch(
366
- updates: Array<{ where: Partial<T>; data: Partial<T> }>,
367
- ): UpdateResult {
368
- return this.updateBuilder.updateBatch(updates);
345
+ updateBatch(updates: Array<{ where: Partial<T>; data: Partial<T> }>): UpdateResult {
346
+ return this.updateBuilder.updateBatch(updates)
369
347
  }
370
348
 
371
349
  // ===== DELETE METHODS (delegated to deleteBuilder) =====
@@ -374,14 +352,14 @@ export class QueryBuilder<T extends Record<string, unknown>> {
374
352
  * Delete rows matching the WHERE conditions.
375
353
  */
376
354
  delete(): DeleteResult {
377
- return this.deleteBuilder.delete();
355
+ return this.deleteBuilder.delete()
378
356
  }
379
357
 
380
358
  /**
381
359
  * Delete and get the deleted rows back.
382
360
  */
383
361
  deleteAndGet(): T[] {
384
- return this.deleteBuilder.deleteAndGet();
362
+ return this.deleteBuilder.deleteAndGet()
385
363
  }
386
364
 
387
365
  /**
@@ -389,43 +367,43 @@ export class QueryBuilder<T extends Record<string, unknown>> {
389
367
  */
390
368
  softDelete(
391
369
  deletedColumn: keyof T = "deleted_at" as keyof T,
392
- deletedValue: SQLQueryBindings = Math.floor(Date.now() / 1000),
370
+ deletedValue: SQLQueryBindings = Math.floor(Date.now() / 1000)
393
371
  ): DeleteResult {
394
- return this.deleteBuilder.softDelete(deletedColumn, deletedValue);
372
+ return this.deleteBuilder.softDelete(deletedColumn, deletedValue)
395
373
  }
396
374
 
397
375
  /**
398
376
  * Restore soft deleted rows.
399
377
  */
400
378
  restore(deletedColumn: keyof T = "deleted_at" as keyof T): DeleteResult {
401
- return this.deleteBuilder.restore(deletedColumn);
379
+ return this.deleteBuilder.restore(deletedColumn)
402
380
  }
403
381
 
404
382
  /**
405
383
  * Batch delete multiple sets of rows.
406
384
  */
407
385
  deleteBatch(conditions: Array<Partial<T>>): DeleteResult {
408
- return this.deleteBuilder.deleteBatch(conditions);
386
+ return this.deleteBuilder.deleteBatch(conditions)
409
387
  }
410
388
 
411
389
  /**
412
390
  * Truncate the entire table (delete all rows).
413
391
  */
414
392
  truncate(): DeleteResult {
415
- return this.deleteBuilder.truncate();
393
+ return this.deleteBuilder.truncate()
416
394
  }
417
395
 
418
396
  /**
419
397
  * Delete rows older than a specified timestamp.
420
398
  */
421
399
  deleteOlderThan(timestampColumn: keyof T, olderThan: number): DeleteResult {
422
- return this.deleteBuilder.deleteOlderThan(timestampColumn, olderThan);
400
+ return this.deleteBuilder.deleteOlderThan(timestampColumn, olderThan)
423
401
  }
424
402
 
425
403
  /**
426
404
  * Delete duplicate rows based on specified columns.
427
405
  */
428
406
  deleteDuplicates(columns: Array<keyof T>): DeleteResult {
429
- return this.deleteBuilder.deleteDuplicates(columns);
407
+ return this.deleteBuilder.deleteDuplicates(columns)
430
408
  }
431
409
  }