@livestore/common 0.3.0-dev.29 → 0.3.0-dev.30

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livestore/common",
3
- "version": "0.3.0-dev.29",
3
+ "version": "0.3.0-dev.30",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "exports": {
@@ -54,7 +54,7 @@
54
54
  "graphology": "0.26.0-alpha1",
55
55
  "graphology-dag": "0.4.1",
56
56
  "graphology-types": "0.24.8",
57
- "@livestore/utils": "0.3.0-dev.29"
57
+ "@livestore/utils": "0.3.0-dev.30"
58
58
  },
59
59
  "devDependencies": {
60
60
  "vitest": "^3.1.1"
@@ -19,7 +19,7 @@ export namespace QueryBuilderAst {
19
19
  export interface SelectQuery {
20
20
  readonly _tag: 'SelectQuery'
21
21
  readonly columns: string[]
22
- readonly pickFirst: false | { fallback: () => any }
22
+ readonly pickFirst: false | { fallback: () => any } | 'no-fallback'
23
23
  readonly select: {
24
24
  columns: ReadonlyArray<string>
25
25
  }
@@ -2,6 +2,7 @@ import { Schema } from '@livestore/utils/effect'
2
2
  import { describe, expect, it } from 'vitest'
3
3
 
4
4
  import { State } from '../schema/mod.js'
5
+ import type { QueryBuilder } from './api.js'
5
6
  import { getResultSchema } from './impl.js'
6
7
 
7
8
  const todos = State.SQLite.table({
@@ -74,236 +75,241 @@ export const issue = State.SQLite.table({
74
75
 
75
76
  const db = { todos, todosWithIntId, comments, issue, UiState, UiStateWithDefaultId }
76
77
 
77
- describe('query builder', () => {
78
- describe('result schema', () => {
79
- it('should print the schema', () => {
80
- expect(String(getResultSchema(db.todos))).toMatchInlineSnapshot(`"ReadonlyArray<todos>"`)
81
- })
82
- })
78
+ const dump = (qb: QueryBuilder<any, any, any>) => ({
79
+ bindValues: qb.asSql().bindValues,
80
+ query: qb.asSql().query,
81
+ schema: getResultSchema(qb).toString(),
82
+ })
83
83
 
84
+ describe('query builder', () => {
84
85
  describe('basic queries', () => {
85
86
  it('should handle simple SELECT queries', () => {
86
- expect(db.todos.asSql()).toMatchInlineSnapshot(`
87
+ expect(dump(db.todos)).toMatchInlineSnapshot(`
87
88
  {
88
89
  "bindValues": [],
89
90
  "query": "SELECT * FROM 'todos'",
91
+ "schema": "ReadonlyArray<todos>",
90
92
  }
91
93
  `)
92
94
 
93
- expect(db.todos.select('id').asSql()).toMatchInlineSnapshot(`
95
+ expect(dump(db.todos.select('id'))).toMatchInlineSnapshot(`
94
96
  {
95
97
  "bindValues": [],
96
98
  "query": "SELECT id FROM 'todos'",
99
+ "schema": "ReadonlyArray<({ readonly id: string } <-> string)>",
97
100
  }
98
101
  `)
99
102
 
100
- expect(db.todos.select('id', 'text').asSql()).toMatchInlineSnapshot(`
103
+ expect(dump(db.todos.select('id', 'text'))).toMatchInlineSnapshot(`
101
104
  {
102
105
  "bindValues": [],
103
106
  "query": "SELECT id, text FROM 'todos'",
107
+ "schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
104
108
  }
105
109
  `)
106
110
  })
107
111
 
108
112
  it('should handle .first()', () => {
109
- expect(db.todos.select('id', 'text').first().asSql()).toMatchInlineSnapshot(`
113
+ expect(dump(db.todos.select('id', 'text').first())).toMatchInlineSnapshot(`
110
114
  {
111
115
  "bindValues": [
112
116
  1,
113
117
  ],
114
118
  "query": "SELECT id, text FROM 'todos' LIMIT ?",
119
+ "schema": "(ReadonlyArray<{ readonly id: string; readonly text: string }> <-> { readonly id: string; readonly text: string })",
115
120
  }
116
121
  `)
117
122
 
118
- expect(
119
- db.todos
120
- .select('id', 'text')
121
- .first({ fallback: () => undefined })
122
- .asSql(),
123
- ).toMatchInlineSnapshot(`
123
+ expect(dump(db.todos.select('id', 'text').first({ fallback: () => undefined }))).toMatchInlineSnapshot(`
124
124
  {
125
125
  "bindValues": [
126
126
  1,
127
127
  ],
128
128
  "query": "SELECT id, text FROM 'todos' LIMIT ?",
129
+ "schema": "(ReadonlyArray<{ readonly id: string; readonly text: string }> | readonly [undefined] <-> { readonly id: string; readonly text: string } | undefined)",
129
130
  }
130
131
  `)
131
132
  })
132
133
 
133
134
  it('should handle WHERE clauses', () => {
134
- expect(db.todos.select('id', 'text').where('completed', true).asSql()).toMatchInlineSnapshot(`
135
+ expect(dump(db.todos.select('id', 'text').where('completed', true))).toMatchInlineSnapshot(`
135
136
  {
136
137
  "bindValues": [
137
138
  1,
138
139
  ],
139
140
  "query": "SELECT id, text FROM 'todos' WHERE completed = ?",
141
+ "schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
140
142
  }
141
143
  `)
142
- expect(db.todos.select('id', 'text').where('completed', '!=', true).asSql()).toMatchInlineSnapshot(`
144
+ expect(dump(db.todos.select('id', 'text').where('completed', '!=', true))).toMatchInlineSnapshot(`
143
145
  {
144
146
  "bindValues": [
145
147
  1,
146
148
  ],
147
149
  "query": "SELECT id, text FROM 'todos' WHERE completed != ?",
150
+ "schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
148
151
  }
149
152
  `)
150
- expect(db.todos.select('id', 'text').where({ completed: true }).asSql()).toMatchInlineSnapshot(`
153
+ expect(dump(db.todos.select('id', 'text').where({ completed: true }))).toMatchInlineSnapshot(`
151
154
  {
152
155
  "bindValues": [
153
156
  1,
154
157
  ],
155
158
  "query": "SELECT id, text FROM 'todos' WHERE completed = ?",
159
+ "schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
156
160
  }
157
161
  `)
158
- expect(db.todos.select('id', 'text').where({ completed: undefined }).asSql()).toMatchInlineSnapshot(`
162
+ expect(dump(db.todos.select('id', 'text').where({ completed: undefined }))).toMatchInlineSnapshot(`
159
163
  {
160
164
  "bindValues": [],
161
165
  "query": "SELECT id, text FROM 'todos'",
166
+ "schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
162
167
  }
163
168
  `)
164
- expect(
165
- db.todos
166
- .select('id', 'text')
167
- .where({ deletedAt: { op: '<=', value: new Date('2024-01-01') } })
168
- .asSql(),
169
- ).toMatchInlineSnapshot(`
170
- {
171
- "bindValues": [
172
- "2024-01-01T00:00:00.000Z",
173
- ],
174
- "query": "SELECT id, text FROM 'todos' WHERE deletedAt <= ?",
175
- }
176
- `)
177
- expect(
178
- db.todos
179
- .select('id', 'text')
180
- .where({ status: { op: 'IN', value: ['active'] } })
181
- .asSql(),
182
- ).toMatchInlineSnapshot(`
183
- {
184
- "bindValues": [
185
- "active",
186
- ],
187
- "query": "SELECT id, text FROM 'todos' WHERE status IN (?)",
188
- }
189
- `)
190
- expect(
191
- db.todos
192
- .select('id', 'text')
193
- .where({ status: { op: 'NOT IN', value: ['active', 'completed'] } })
194
- .asSql(),
195
- ).toMatchInlineSnapshot(`
196
- {
197
- "bindValues": [
198
- "active",
199
- "completed",
200
- ],
201
- "query": "SELECT id, text FROM 'todos' WHERE status NOT IN (?, ?)",
202
- }
203
- `)
204
- })
205
-
206
- it('should handle OFFSET and LIMIT clauses', () => {
207
- expect(db.todos.select('id', 'text').where('completed', true).offset(10).limit(10).asSql())
169
+ expect(dump(db.todos.select('id', 'text').where({ deletedAt: { op: '<=', value: new Date('2024-01-01') } })))
208
170
  .toMatchInlineSnapshot(`
209
171
  {
210
172
  "bindValues": [
211
- 1,
212
- 10,
213
- 10,
173
+ "2024-01-01T00:00:00.000Z",
214
174
  ],
215
- "query": "SELECT id, text FROM 'todos' WHERE completed = ? OFFSET ? LIMIT ?",
175
+ "query": "SELECT id, text FROM 'todos' WHERE deletedAt <= ?",
176
+ "schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
216
177
  }
217
178
  `)
218
- })
219
-
220
- it('should handle OFFSET and LIMIT clauses correctly', () => {
221
- // Test with both offset and limit
222
- expect(db.todos.select('id', 'text').where('completed', true).offset(5).limit(10).asSql()).toMatchInlineSnapshot(`
179
+ expect(dump(db.todos.select('id', 'text').where({ status: { op: 'IN', value: ['active'] } })))
180
+ .toMatchInlineSnapshot(`
223
181
  {
224
182
  "bindValues": [
225
- 1,
226
- 5,
227
- 10,
183
+ "active",
228
184
  ],
229
- "query": "SELECT id, text FROM 'todos' WHERE completed = ? OFFSET ? LIMIT ?",
185
+ "query": "SELECT id, text FROM 'todos' WHERE status IN (?)",
186
+ "schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
230
187
  }
231
188
  `)
232
-
233
- // Test with only offset
234
- expect(db.todos.select('id', 'text').where('completed', true).offset(5).asSql()).toMatchInlineSnapshot(`
189
+ expect(dump(db.todos.select('id', 'text').where({ status: { op: 'NOT IN', value: ['active', 'completed'] } })))
190
+ .toMatchInlineSnapshot(`
235
191
  {
236
192
  "bindValues": [
237
- 1,
238
- 5,
193
+ "active",
194
+ "completed",
239
195
  ],
240
- "query": "SELECT id, text FROM 'todos' WHERE completed = ? OFFSET ?",
196
+ "query": "SELECT id, text FROM 'todos' WHERE status NOT IN (?, ?)",
197
+ "schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
241
198
  }
242
199
  `)
200
+ })
201
+
202
+ it('should handle OFFSET and LIMIT clauses', () => {
203
+ expect(dump(db.todos.select('id', 'text').where('completed', true).offset(10).limit(10))).toMatchInlineSnapshot(`
204
+ {
205
+ "bindValues": [
206
+ 1,
207
+ 10,
208
+ 10,
209
+ ],
210
+ "query": "SELECT id, text FROM 'todos' WHERE completed = ? OFFSET ? LIMIT ?",
211
+ "schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
212
+ }
213
+ `)
214
+ })
215
+
216
+ it('should handle OFFSET and LIMIT clauses correctly', () => {
217
+ // Test with both offset and limit
218
+ expect(dump(db.todos.select('id', 'text').where('completed', true).offset(5).limit(10))).toMatchInlineSnapshot(`
219
+ {
220
+ "bindValues": [
221
+ 1,
222
+ 5,
223
+ 10,
224
+ ],
225
+ "query": "SELECT id, text FROM 'todos' WHERE completed = ? OFFSET ? LIMIT ?",
226
+ "schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
227
+ }
228
+ `)
229
+
230
+ // Test with only offset
231
+ expect(dump(db.todos.select('id', 'text').where('completed', true).offset(5))).toMatchInlineSnapshot(`
232
+ {
233
+ "bindValues": [
234
+ 1,
235
+ 5,
236
+ ],
237
+ "query": "SELECT id, text FROM 'todos' WHERE completed = ? OFFSET ?",
238
+ "schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
239
+ }
240
+ `)
243
241
 
244
242
  // Test with only limit
245
- expect(db.todos.select('id', 'text').where('completed', true).limit(10).asSql()).toMatchInlineSnapshot(`
246
- {
247
- "bindValues": [
248
- 1,
249
- 10,
250
- ],
251
- "query": "SELECT id, text FROM 'todos' WHERE completed = ? LIMIT ?",
252
- }
253
- `)
243
+ expect(dump(db.todos.select('id', 'text').where('completed', true).limit(10))).toMatchInlineSnapshot(`
244
+ {
245
+ "bindValues": [
246
+ 1,
247
+ 10,
248
+ ],
249
+ "query": "SELECT id, text FROM 'todos' WHERE completed = ? LIMIT ?",
250
+ "schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
251
+ }
252
+ `)
254
253
  })
255
254
 
256
255
  it('should handle COUNT queries', () => {
257
- expect(db.todos.count().asSql()).toMatchInlineSnapshot(`
256
+ expect(dump(db.todos.count())).toMatchInlineSnapshot(`
258
257
  {
259
258
  "bindValues": [],
260
259
  "query": "SELECT COUNT(*) as count FROM 'todos'",
260
+ "schema": "(ReadonlyArray<({ readonly count: number } <-> number)> <-> number)",
261
261
  }
262
262
  `)
263
- expect(db.todos.count().where('completed', true).asSql()).toMatchInlineSnapshot(`
263
+ expect(dump(db.todos.count().where('completed', true))).toMatchInlineSnapshot(`
264
264
  {
265
265
  "bindValues": [
266
266
  1,
267
267
  ],
268
268
  "query": "SELECT COUNT(*) as count FROM 'todos' WHERE completed = ?",
269
+ "schema": "(ReadonlyArray<({ readonly count: number } <-> number)> <-> number)",
269
270
  }
270
271
  `)
271
272
  })
272
273
 
273
274
  it('should handle NULL comparisons', () => {
274
- expect(db.todos.select('id', 'text').where('deletedAt', '=', null).asSql()).toMatchInlineSnapshot(`
275
+ expect(dump(db.todos.select('id', 'text').where('deletedAt', '=', null))).toMatchInlineSnapshot(`
275
276
  {
276
277
  "bindValues": [],
277
278
  "query": "SELECT id, text FROM 'todos' WHERE deletedAt IS NULL",
279
+ "schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
278
280
  }
279
281
  `)
280
- expect(db.todos.select('id', 'text').where('deletedAt', '!=', null).asSql()).toMatchInlineSnapshot(`
282
+ expect(dump(db.todos.select('id', 'text').where('deletedAt', '!=', null))).toMatchInlineSnapshot(`
281
283
  {
282
284
  "bindValues": [],
283
285
  "query": "SELECT id, text FROM 'todos' WHERE deletedAt IS NOT NULL",
286
+ "schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
284
287
  }
285
288
  `)
286
289
  })
287
290
 
288
291
  it('should handle orderBy', () => {
289
- expect(db.todos.orderBy('completed', 'desc').asSql()).toMatchInlineSnapshot(`
292
+ expect(dump(db.todos.orderBy('completed', 'desc'))).toMatchInlineSnapshot(`
290
293
  {
291
294
  "bindValues": [],
292
295
  "query": "SELECT * FROM 'todos' ORDER BY completed desc",
296
+ "schema": "ReadonlyArray<todos>",
293
297
  }
294
298
  `)
295
299
 
296
- expect(db.todos.orderBy([{ col: 'completed', direction: 'desc' }]).asSql()).toMatchInlineSnapshot(`
300
+ expect(dump(db.todos.orderBy([{ col: 'completed', direction: 'desc' }]))).toMatchInlineSnapshot(`
297
301
  {
298
302
  "bindValues": [],
299
303
  "query": "SELECT * FROM 'todos' ORDER BY completed desc",
304
+ "schema": "ReadonlyArray<todos>",
300
305
  }
301
306
  `)
302
307
 
303
- expect(db.todos.orderBy([]).asSql()).toMatchInlineSnapshot(`
308
+ expect(dump(db.todos.orderBy([]))).toMatchInlineSnapshot(`
304
309
  {
305
310
  "bindValues": [],
306
311
  "query": "SELECT * FROM 'todos'",
312
+ "schema": "ReadonlyArray<todos>",
307
313
  }
308
314
  `)
309
315
  })
@@ -311,31 +317,34 @@ describe('query builder', () => {
311
317
 
312
318
  // describe('getOrCreate queries', () => {
313
319
  // it('should handle getOrCreate queries', () => {
314
- // expect(db.UiState.getOrCreate('sessionid-1').asSql()).toMatchInlineSnapshot(`
320
+ // expect(dump(db.UiState.getOrCreate('sessionid-1'))).toMatchInlineSnapshot(`
315
321
  // {
316
322
  // "bindValues": [
317
323
  // "sessionid-1",
318
324
  // ],
319
325
  // "query": "SELECT * FROM 'UiState' WHERE id = ?",
326
+ // "schema": "...", // TODO determine schema
320
327
  // }
321
328
  // `)
322
329
  // })
323
330
 
324
331
  // it('should handle getOrCreate queries with default id', () => {
325
- // expect(db.UiStateWithDefaultId.getOrCreate().asSql()).toMatchInlineSnapshot(`
332
+ // expect(dump(db.UiStateWithDefaultId.getOrCreate())).toMatchInlineSnapshot(`
326
333
  // {
327
334
  // "bindValues": [],
328
335
  // "query": "SELECT * FROM 'UiState' WHERE id = ?",
336
+ // "schema": "...", // TODO determine schema
329
337
  // }
330
338
  // `)
331
339
  // })
332
340
  // // it('should handle row queries with numbers', () => {
333
- // // expect(db.todosWithIntId.getOrCreate(123, { insertValues: { status: 'active' } }).asSql()).toMatchInlineSnapshot(`
341
+ // // expect(dump(db.todosWithIntId.getOrCreate(123, { insertValues: { status: 'active' } }))).toMatchInlineSnapshot(`
334
342
  // // {
335
343
  // // "bindValues": [
336
344
  // // 123,
337
345
  // // ],
338
346
  // // "query": "SELECT * FROM 'todos_with_int_id' WHERE id = ?",
347
+ // // "schema": "...", // TODO determine schema
339
348
  // // }
340
349
  // // `)
341
350
  // // })
@@ -343,7 +352,7 @@ describe('query builder', () => {
343
352
 
344
353
  describe('write operations', () => {
345
354
  it('should handle INSERT queries', () => {
346
- expect(db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' }).asSql()).toMatchInlineSnapshot(`
355
+ expect(dump(db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' }))).toMatchInlineSnapshot(`
347
356
  {
348
357
  "bindValues": [
349
358
  "123",
@@ -351,12 +360,13 @@ describe('query builder', () => {
351
360
  "active",
352
361
  ],
353
362
  "query": "INSERT INTO 'todos' (id, text, status) VALUES (?, ?, ?)",
363
+ "schema": "number",
354
364
  }
355
365
  `)
356
366
  })
357
367
 
358
368
  it('should handle INSERT queries with undefined values', () => {
359
- expect(db.todos.insert({ id: '123', text: 'Buy milk', status: 'active', completed: undefined }).asSql())
369
+ expect(dump(db.todos.insert({ id: '123', text: 'Buy milk', status: 'active', completed: undefined })))
360
370
  .toMatchInlineSnapshot(`
361
371
  {
362
372
  "bindValues": [
@@ -365,6 +375,7 @@ describe('query builder', () => {
365
375
  "active",
366
376
  ],
367
377
  "query": "INSERT INTO 'todos' (id, text, status) VALUES (?, ?, ?)",
378
+ "schema": "number",
368
379
  }
369
380
  `)
370
381
  })
@@ -372,8 +383,8 @@ describe('query builder', () => {
372
383
  // Test helped to catch a bindValues ordering bug
373
384
  it('should handle INSERT queries (issue)', () => {
374
385
  expect(
375
- db.issue
376
- .insert({
386
+ dump(
387
+ db.issue.insert({
377
388
  id: 1,
378
389
  title: 'Revert the user profile page',
379
390
  priority: 2,
@@ -381,8 +392,8 @@ describe('query builder', () => {
381
392
  modified: new Date('2024-12-29T17:15:20.507Z'),
382
393
  kanbanorder: 'a2',
383
394
  creator: 'John Doe',
384
- })
385
- .asSql(),
395
+ }),
396
+ ),
386
397
  ).toMatchInlineSnapshot(`
387
398
  {
388
399
  "bindValues": [
@@ -395,32 +406,35 @@ describe('query builder', () => {
395
406
  "John Doe",
396
407
  ],
397
408
  "query": "INSERT INTO 'issue' (id, title, priority, created, modified, kanbanorder, creator) VALUES (?, ?, ?, ?, ?, ?, ?)",
409
+ "schema": "number",
398
410
  }
399
411
  `)
400
412
  })
401
413
 
402
414
  it('should handle UPDATE queries', () => {
403
- expect(db.todos.update({ status: 'completed' }).where({ id: '123' }).asSql()).toMatchInlineSnapshot(`
415
+ expect(dump(db.todos.update({ status: 'completed' }).where({ id: '123' }))).toMatchInlineSnapshot(`
404
416
  {
405
417
  "bindValues": [
406
418
  "completed",
407
419
  "123",
408
420
  ],
409
421
  "query": "UPDATE 'todos' SET status = ? WHERE id = ?",
422
+ "schema": "number",
410
423
  }
411
424
  `)
412
425
 
413
426
  // empty update set
414
- expect(db.todos.update({}).where({ id: '123' }).asSql()).toMatchInlineSnapshot(`
427
+ expect(dump(db.todos.update({}).where({ id: '123' }))).toMatchInlineSnapshot(`
415
428
  {
416
429
  "bindValues": [],
417
430
  "query": "SELECT 1",
431
+ "schema": "number",
418
432
  }
419
433
  `)
420
434
  })
421
435
 
422
436
  it('should handle UPDATE queries with undefined values', () => {
423
- expect(db.todos.update({ status: undefined, text: 'some text' }).where({ id: '123' }).asSql())
437
+ expect(dump(db.todos.update({ status: undefined, text: 'some text' }).where({ id: '123' })))
424
438
  .toMatchInlineSnapshot(`
425
439
  {
426
440
  "bindValues": [
@@ -428,12 +442,13 @@ describe('query builder', () => {
428
442
  "123",
429
443
  ],
430
444
  "query": "UPDATE 'todos' SET text = ? WHERE id = ?",
445
+ "schema": "number",
431
446
  }
432
447
  `)
433
448
  })
434
449
 
435
450
  it('should handle UPDATE queries with undefined values (issue)', () => {
436
- expect(db.issue.update({ priority: 2, creator: 'John Doe' }).where({ id: 1 }).asSql()).toMatchInlineSnapshot(`
451
+ expect(dump(db.issue.update({ priority: 2, creator: 'John Doe' }).where({ id: 1 }))).toMatchInlineSnapshot(`
437
452
  {
438
453
  "bindValues": [
439
454
  2,
@@ -441,23 +456,25 @@ describe('query builder', () => {
441
456
  1,
442
457
  ],
443
458
  "query": "UPDATE 'issue' SET priority = ?, creator = ? WHERE id = ?",
459
+ "schema": "number",
444
460
  }
445
461
  `)
446
462
  })
447
463
 
448
464
  it('should handle DELETE queries', () => {
449
- expect(db.todos.delete().where({ status: 'completed' }).asSql()).toMatchInlineSnapshot(`
465
+ expect(dump(db.todos.delete().where({ status: 'completed' }))).toMatchInlineSnapshot(`
450
466
  {
451
467
  "bindValues": [
452
468
  "completed",
453
469
  ],
454
470
  "query": "DELETE FROM 'todos' WHERE status = ?",
471
+ "schema": "number",
455
472
  }
456
473
  `)
457
474
  })
458
475
 
459
476
  it('should handle INSERT with ON CONFLICT', () => {
460
- expect(db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' }).onConflict('id', 'ignore').asSql())
477
+ expect(dump(db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' }).onConflict('id', 'ignore')))
461
478
  .toMatchInlineSnapshot(`
462
479
  {
463
480
  "bindValues": [
@@ -466,14 +483,16 @@ describe('query builder', () => {
466
483
  "active",
467
484
  ],
468
485
  "query": "INSERT INTO 'todos' (id, text, status) VALUES (?, ?, ?) ON CONFLICT (id) DO NOTHING",
486
+ "schema": "number",
469
487
  }
470
488
  `)
471
489
 
472
490
  expect(
473
- db.todos
474
- .insert({ id: '123', text: 'Buy milk', status: 'active' })
475
- .onConflict('id', 'update', { text: 'Buy soy milk', status: 'active' })
476
- .asSql(),
491
+ dump(
492
+ db.todos
493
+ .insert({ id: '123', text: 'Buy milk', status: 'active' })
494
+ .onConflict('id', 'update', { text: 'Buy soy milk', status: 'active' }),
495
+ ),
477
496
  ).toMatchInlineSnapshot(`
478
497
  {
479
498
  "bindValues": [
@@ -484,10 +503,11 @@ describe('query builder', () => {
484
503
  "active",
485
504
  ],
486
505
  "query": "INSERT INTO 'todos' (id, text, status) VALUES (?, ?, ?) ON CONFLICT (id) DO UPDATE SET text = ?, status = ?",
506
+ "schema": "number",
487
507
  }
488
508
  `)
489
509
 
490
- expect(db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' }).onConflict('id', 'replace').asSql())
510
+ expect(dump(db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' }).onConflict('id', 'replace')))
491
511
  .toMatchInlineSnapshot(`
492
512
  {
493
513
  "bindValues": [
@@ -496,16 +516,14 @@ describe('query builder', () => {
496
516
  "active",
497
517
  ],
498
518
  "query": "INSERT OR REPLACE INTO 'todos' (id, text, status) VALUES (?, ?, ?)",
519
+ "schema": "number",
499
520
  }
500
521
  `)
501
522
  })
502
523
 
503
524
  it('should handle ON CONFLICT with multiple columns', () => {
504
525
  expect(
505
- db.todos
506
- .insert({ id: '123', text: 'Buy milk', status: 'active' })
507
- .onConflict(['id', 'status'], 'ignore')
508
- .asSql(),
526
+ dump(db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' }).onConflict(['id', 'status'], 'ignore')),
509
527
  ).toMatchInlineSnapshot(`
510
528
  {
511
529
  "bindValues": [
@@ -514,40 +532,44 @@ describe('query builder', () => {
514
532
  "active",
515
533
  ],
516
534
  "query": "INSERT INTO 'todos' (id, text, status) VALUES (?, ?, ?) ON CONFLICT (id, status) DO NOTHING",
535
+ "schema": "number",
517
536
  }
518
537
  `)
519
538
  })
520
539
 
521
540
  it('should handle RETURNING clause', () => {
522
- expect(db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' }).returning('id').asSql())
541
+ expect(dump(db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' }).returning('id')))
523
542
  .toMatchInlineSnapshot(`
524
- {
525
- "bindValues": [
526
- "123",
527
- "Buy milk",
528
- "active",
529
- ],
530
- "query": "INSERT INTO 'todos' (id, text, status) VALUES (?, ?, ?) RETURNING id",
531
- }
532
- `)
543
+ {
544
+ "bindValues": [
545
+ "123",
546
+ "Buy milk",
547
+ "active",
548
+ ],
549
+ "query": "INSERT INTO 'todos' (id, text, status) VALUES (?, ?, ?) RETURNING id",
550
+ "schema": "ReadonlyArray<{ readonly id: string }>",
551
+ }
552
+ `)
533
553
 
534
- expect(db.todos.update({ status: 'completed' }).where({ id: '123' }).returning('id').asSql())
554
+ expect(dump(db.todos.update({ status: 'completed' }).where({ id: '123' }).returning('id')))
535
555
  .toMatchInlineSnapshot(`
536
- {
537
- "bindValues": [
538
- "completed",
539
- "123",
540
- ],
541
- "query": "UPDATE 'todos' SET status = ? WHERE id = ? RETURNING id",
542
- }
543
- `)
556
+ {
557
+ "bindValues": [
558
+ "completed",
559
+ "123",
560
+ ],
561
+ "query": "UPDATE 'todos' SET status = ? WHERE id = ? RETURNING id",
562
+ "schema": "ReadonlyArray<{ readonly id: string }>",
563
+ }
564
+ `)
544
565
 
545
- expect(db.todos.delete().where({ status: 'completed' }).returning('id').asSql()).toMatchInlineSnapshot(`
566
+ expect(dump(db.todos.delete().where({ status: 'completed' }).returning('id'))).toMatchInlineSnapshot(`
546
567
  {
547
568
  "bindValues": [
548
569
  "completed",
549
570
  ],
550
571
  "query": "DELETE FROM 'todos' WHERE status = ? RETURNING id",
572
+ "schema": "ReadonlyArray<{ readonly id: string }>",
551
573
  }
552
574
  `)
553
575
  })