@symbiote-native/sqlite 0.1.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.
Files changed (128) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +283 -0
  3. package/build/angular/index.d.ts +2 -0
  4. package/build/angular/index.js +2 -0
  5. package/build/angular/sqlite.service.d.ts +41 -0
  6. package/build/angular/sqlite.service.js +127 -0
  7. package/build/core/index.d.ts +10 -0
  8. package/build/core/index.js +6 -0
  9. package/build/core/native-database.d.ts +65 -0
  10. package/build/core/native-database.js +13 -0
  11. package/build/core/native-module.d.ts +25 -0
  12. package/build/core/native-module.js +17 -0
  13. package/build/core/native-session.d.ts +20 -0
  14. package/build/core/native-session.js +1 -0
  15. package/build/core/native-statement.d.ts +45 -0
  16. package/build/core/native-statement.js +6 -0
  17. package/build/core/param-utils.d.ts +28 -0
  18. package/build/core/param-utils.js +126 -0
  19. package/build/core/path-utils.d.ts +6 -0
  20. package/build/core/path-utils.js +29 -0
  21. package/build/core/query-utils.d.ts +17 -0
  22. package/build/core/query-utils.js +37 -0
  23. package/build/core/sqlite-database.d.ts +246 -0
  24. package/build/core/sqlite-database.js +427 -0
  25. package/build/core/sqlite-session.d.ts +84 -0
  26. package/build/core/sqlite-session.js +121 -0
  27. package/build/core/sqlite-statement.d.ts +96 -0
  28. package/build/core/sqlite-statement.js +321 -0
  29. package/build/core/sqlite-tagged-query.d.ts +73 -0
  30. package/build/core/sqlite-tagged-query.js +119 -0
  31. package/build/core/storage.d.ts +107 -0
  32. package/build/core/storage.js +382 -0
  33. package/build/core/types.d.ts +30 -0
  34. package/build/core/types.js +1 -0
  35. package/build/react/index.d.ts +2 -0
  36. package/build/react/index.js +2 -0
  37. package/build/react/sqlite-context.d.ts +25 -0
  38. package/build/react/sqlite-context.js +129 -0
  39. package/build/solid/index.d.ts +2 -0
  40. package/build/solid/index.js +2 -0
  41. package/build/solid/sqlite-context.d.ts +23 -0
  42. package/build/solid/sqlite-context.js +106 -0
  43. package/build/svelte/SQLiteProvider.svelte +88 -0
  44. package/build/svelte/SQLiteProvider.svelte.d.ts +13 -0
  45. package/build/svelte/index.d.ts +3 -0
  46. package/build/svelte/index.js +5 -0
  47. package/build/svelte/sqlite-context.d.ts +11 -0
  48. package/build/svelte/sqlite-context.js +23 -0
  49. package/build/vue/index.d.ts +2 -0
  50. package/build/vue/index.js +2 -0
  51. package/build/vue/sqlite-context.d.ts +21 -0
  52. package/build/vue/sqlite-context.js +69 -0
  53. package/build-ngc/angular/index.d.ts +2 -0
  54. package/build-ngc/angular/index.js +3 -0
  55. package/build-ngc/angular/index.js.map +1 -0
  56. package/build-ngc/angular/sqlite.service.d.ts +44 -0
  57. package/build-ngc/angular/sqlite.service.js +85 -0
  58. package/build-ngc/angular/sqlite.service.js.map +1 -0
  59. package/build-ngc/core/index.d.ts +10 -0
  60. package/build-ngc/core/index.js +7 -0
  61. package/build-ngc/core/index.js.map +1 -0
  62. package/build-ngc/core/native-database.d.ts +65 -0
  63. package/build-ngc/core/native-database.js +14 -0
  64. package/build-ngc/core/native-database.js.map +1 -0
  65. package/build-ngc/core/native-module.d.ts +25 -0
  66. package/build-ngc/core/native-module.js +18 -0
  67. package/build-ngc/core/native-module.js.map +1 -0
  68. package/build-ngc/core/native-session.d.ts +20 -0
  69. package/build-ngc/core/native-session.js +2 -0
  70. package/build-ngc/core/native-session.js.map +1 -0
  71. package/build-ngc/core/native-statement.d.ts +45 -0
  72. package/build-ngc/core/native-statement.js +7 -0
  73. package/build-ngc/core/native-statement.js.map +1 -0
  74. package/build-ngc/core/param-utils.d.ts +28 -0
  75. package/build-ngc/core/param-utils.js +127 -0
  76. package/build-ngc/core/param-utils.js.map +1 -0
  77. package/build-ngc/core/path-utils.d.ts +6 -0
  78. package/build-ngc/core/path-utils.js +30 -0
  79. package/build-ngc/core/path-utils.js.map +1 -0
  80. package/build-ngc/core/query-utils.d.ts +17 -0
  81. package/build-ngc/core/query-utils.js +38 -0
  82. package/build-ngc/core/query-utils.js.map +1 -0
  83. package/build-ngc/core/sqlite-database.d.ts +246 -0
  84. package/build-ngc/core/sqlite-database.js +428 -0
  85. package/build-ngc/core/sqlite-database.js.map +1 -0
  86. package/build-ngc/core/sqlite-session.d.ts +84 -0
  87. package/build-ngc/core/sqlite-session.js +122 -0
  88. package/build-ngc/core/sqlite-session.js.map +1 -0
  89. package/build-ngc/core/sqlite-statement.d.ts +96 -0
  90. package/build-ngc/core/sqlite-statement.js +322 -0
  91. package/build-ngc/core/sqlite-statement.js.map +1 -0
  92. package/build-ngc/core/sqlite-tagged-query.d.ts +73 -0
  93. package/build-ngc/core/sqlite-tagged-query.js +120 -0
  94. package/build-ngc/core/sqlite-tagged-query.js.map +1 -0
  95. package/build-ngc/core/storage.d.ts +107 -0
  96. package/build-ngc/core/storage.js +383 -0
  97. package/build-ngc/core/storage.js.map +1 -0
  98. package/build-ngc/core/types.d.ts +30 -0
  99. package/build-ngc/core/types.js +2 -0
  100. package/build-ngc/core/types.js.map +1 -0
  101. package/native-link.json +12 -0
  102. package/package.json +141 -0
  103. package/src/angular/index.ts +2 -0
  104. package/src/angular/sqlite.service.ts +114 -0
  105. package/src/core/index.ts +40 -0
  106. package/src/core/native-database.ts +118 -0
  107. package/src/core/native-module.ts +57 -0
  108. package/src/core/native-session.ts +64 -0
  109. package/src/core/native-statement.ts +85 -0
  110. package/src/core/param-utils.ts +163 -0
  111. package/src/core/path-utils.ts +38 -0
  112. package/src/core/query-utils.ts +45 -0
  113. package/src/core/sqlite-database.ts +676 -0
  114. package/src/core/sqlite-session.ts +165 -0
  115. package/src/core/sqlite-statement.ts +578 -0
  116. package/src/core/sqlite-tagged-query.ts +160 -0
  117. package/src/core/storage.ts +492 -0
  118. package/src/core/types.ts +33 -0
  119. package/src/react/index.ts +2 -0
  120. package/src/react/sqlite-context.tsx +244 -0
  121. package/src/solid/index.ts +2 -0
  122. package/src/solid/sqlite-context.ts +156 -0
  123. package/src/svelte/SQLiteProvider.svelte +88 -0
  124. package/src/svelte/index.ts +6 -0
  125. package/src/svelte/sqlite-context.ts +32 -0
  126. package/src/svelte/svelte-compile.test-helper.ts +135 -0
  127. package/src/vue/index.ts +2 -0
  128. package/src/vue/sqlite-context.ts +105 -0
@@ -0,0 +1,578 @@
1
+ // Ported from expo-sqlite's SQLiteStatement.ts (.vendors/expo @ origin/sdk-57,
2
+ // packages/expo-sqlite/src/SQLiteStatement.ts), renamed with this repo's `I`-prefix convention.
3
+ import type { NativeDatabase } from './native-database';
4
+ import type {
5
+ NativeStatement,
6
+ ISQLiteBindParams,
7
+ ISQLiteVariadicBindParams,
8
+ ISQLiteAnyDatabase,
9
+ ISQLiteColumnNames,
10
+ ISQLiteColumnValues,
11
+ } from './native-statement';
12
+ import { composeRow, composeRows, normalizeParams } from './param-utils';
13
+
14
+ export type {
15
+ ISQLiteBindParams,
16
+ ISQLiteBindValue,
17
+ ISQLiteRunResult,
18
+ ISQLiteVariadicBindParams,
19
+ } from './native-statement';
20
+
21
+ type IValuesOf<T extends object> = T[keyof T][];
22
+
23
+ /**
24
+ * A prepared statement returned by `SQLiteDatabase.prepareAsync()`/`prepareSync()`, bound with
25
+ * parameters and executed.
26
+ */
27
+ export class SQLiteStatement {
28
+ constructor(
29
+ private readonly nativeDatabase: NativeDatabase,
30
+ private readonly nativeStatement: NativeStatement,
31
+ ) {}
32
+
33
+ //#region Asynchronous API
34
+
35
+ /** Runs the prepared statement and returns an `ISQLiteExecuteAsyncResult`. */
36
+ public executeAsync<T>(
37
+ params: ISQLiteBindParams,
38
+ ): Promise<ISQLiteExecuteAsyncResult<T>>;
39
+ /** @hidden */
40
+ public executeAsync<T>(
41
+ ...params: ISQLiteVariadicBindParams
42
+ ): Promise<ISQLiteExecuteAsyncResult<T>>;
43
+ public async executeAsync<T>(
44
+ ...params: unknown[]
45
+ ): Promise<ISQLiteExecuteAsyncResult<T>> {
46
+ const { lastInsertRowId, changes, firstRowValues } =
47
+ await this.nativeStatement.runAsync(
48
+ this.nativeDatabase,
49
+ ...normalizeParams(...params),
50
+ );
51
+ return createSQLiteExecuteAsyncResult<T>(
52
+ this.nativeDatabase,
53
+ this.nativeStatement,
54
+ firstRowValues,
55
+ { rawResult: false, lastInsertRowId, changes },
56
+ );
57
+ }
58
+
59
+ /**
60
+ * Like `executeAsync()` but returns the raw value-array result instead of row objects.
61
+ * @hidden Advanced use only.
62
+ */
63
+ public executeForRawResultAsync<T extends object>(
64
+ params: ISQLiteBindParams,
65
+ ): Promise<ISQLiteExecuteAsyncResult<IValuesOf<T>>>;
66
+ /** @hidden */
67
+ public executeForRawResultAsync<T extends object>(
68
+ ...params: ISQLiteVariadicBindParams
69
+ ): Promise<ISQLiteExecuteAsyncResult<IValuesOf<T>>>;
70
+ public async executeForRawResultAsync<T extends object>(
71
+ ...params: unknown[]
72
+ ): Promise<ISQLiteExecuteAsyncResult<IValuesOf<T>>> {
73
+ const { lastInsertRowId, changes, firstRowValues } =
74
+ await this.nativeStatement.runAsync(
75
+ this.nativeDatabase,
76
+ ...normalizeParams(...params),
77
+ );
78
+ return createSQLiteExecuteAsyncResult<IValuesOf<T>>(
79
+ this.nativeDatabase,
80
+ this.nativeStatement,
81
+ firstRowValues,
82
+ { rawResult: true, lastInsertRowId, changes },
83
+ );
84
+ }
85
+
86
+ /** Gets the column names of the prepared statement. */
87
+ public getColumnNamesAsync(): Promise<string[]> {
88
+ return this.nativeStatement.getColumnNamesAsync();
89
+ }
90
+
91
+ /**
92
+ * Finalizes the prepared statement — calls
93
+ * [`sqlite3_finalize()`](https://www.sqlite.org/c3ref/finalize.html) under the hood.
94
+ * Accessing a finalized statement afterward throws. `expo-sqlite` finalizes any orphaned
95
+ * statement automatically when its database closes, but finalize as soon as you no longer
96
+ * need one — wrap the call in `try...finally` so it runs even if an earlier step throws.
97
+ */
98
+ public async finalizeAsync(): Promise<void> {
99
+ await this.nativeStatement.finalizeAsync(this.nativeDatabase);
100
+ }
101
+
102
+ //#endregion
103
+
104
+ //#region Synchronous API
105
+
106
+ /**
107
+ * Runs the prepared statement and returns an `ISQLiteExecuteSyncResult`.
108
+ * > **Note:** Running heavy tasks with this function can block the JavaScript thread.
109
+ */
110
+ public executeSync<T>(params: ISQLiteBindParams): ISQLiteExecuteSyncResult<T>;
111
+ /** @hidden */
112
+ public executeSync<T>(
113
+ ...params: ISQLiteVariadicBindParams
114
+ ): ISQLiteExecuteSyncResult<T>;
115
+ public executeSync<T>(...params: unknown[]): ISQLiteExecuteSyncResult<T> {
116
+ const { lastInsertRowId, changes, firstRowValues } =
117
+ this.nativeStatement.runSync(
118
+ this.nativeDatabase,
119
+ ...normalizeParams(...params),
120
+ );
121
+ return createSQLiteExecuteSyncResult<T>(
122
+ this.nativeDatabase,
123
+ this.nativeStatement,
124
+ firstRowValues,
125
+ { rawResult: false, lastInsertRowId, changes },
126
+ );
127
+ }
128
+
129
+ /**
130
+ * Like `executeSync()` but returns the raw value-array result instead of row objects.
131
+ * @hidden Advanced use only.
132
+ */
133
+ public executeForRawResultSync<T extends object>(
134
+ params: ISQLiteBindParams,
135
+ ): ISQLiteExecuteSyncResult<IValuesOf<T>>;
136
+ /** @hidden */
137
+ public executeForRawResultSync<T extends object>(
138
+ ...params: ISQLiteVariadicBindParams
139
+ ): ISQLiteExecuteSyncResult<IValuesOf<T>>;
140
+ public executeForRawResultSync<T extends object>(
141
+ ...params: unknown[]
142
+ ): ISQLiteExecuteSyncResult<IValuesOf<T>> {
143
+ const { lastInsertRowId, changes, firstRowValues } =
144
+ this.nativeStatement.runSync(
145
+ this.nativeDatabase,
146
+ ...normalizeParams(...params),
147
+ );
148
+ return createSQLiteExecuteSyncResult<IValuesOf<T>>(
149
+ this.nativeDatabase,
150
+ this.nativeStatement,
151
+ firstRowValues,
152
+ { rawResult: true, lastInsertRowId, changes },
153
+ );
154
+ }
155
+
156
+ /** Gets the column names of the prepared statement. */
157
+ public getColumnNamesSync(): string[] {
158
+ return this.nativeStatement.getColumnNamesSync();
159
+ }
160
+
161
+ /**
162
+ * Finalizes the prepared statement synchronously.
163
+ * > **Note:** Running heavy tasks with this function can block the JavaScript thread.
164
+ */
165
+ public finalizeSync(): void {
166
+ this.nativeStatement.finalizeSync(this.nativeDatabase);
167
+ }
168
+
169
+ //#endregion
170
+ }
171
+
172
+ /** A result returned by `SQLiteStatement.executeAsync()`. */
173
+ export type ISQLiteExecuteAsyncResult<T> = AsyncIterableIterator<T> & {
174
+ /** The last inserted row ID, from `sqlite3_last_insert_rowid()`. */
175
+ readonly lastInsertRowId: number;
176
+ /** The number of rows affected, from `sqlite3_changes()`. */
177
+ readonly changes: number;
178
+ /**
179
+ * Gets the first row of the result set. Requires the cursor to be in its initial state —
180
+ * call `resetAsync()` first if rows have already been retrieved, or this throws.
181
+ */
182
+ getFirstAsync(): Promise<T | null>;
183
+ /**
184
+ * Gets all rows of the result set. Requires the cursor to be in its initial state — call
185
+ * `resetAsync()` first if rows have already been retrieved, or this throws.
186
+ */
187
+ getAllAsync(): Promise<T[]>;
188
+ /** Resets the prepared statement's cursor — calls `sqlite3_reset()` under the hood. */
189
+ resetAsync(): Promise<void>;
190
+ };
191
+
192
+ /**
193
+ * A result returned by `SQLiteStatement.executeSync()`.
194
+ * > **Note:** Running heavy tasks with this function can block the JavaScript thread.
195
+ */
196
+ export type ISQLiteExecuteSyncResult<T> = IterableIterator<T> & {
197
+ /** The last inserted row ID, from `sqlite3_last_insert_rowid()`. */
198
+ readonly lastInsertRowId: number;
199
+ /** The number of rows affected, from `sqlite3_changes()`. */
200
+ readonly changes: number;
201
+ /**
202
+ * Gets the first row of the result set. Requires the cursor to be in its initial state — call
203
+ * `resetSync()` first if rows have already been retrieved, or this throws.
204
+ */
205
+ getFirstSync(): T | null;
206
+ /**
207
+ * Gets all rows of the result set. Requires the cursor to be in its initial state — call
208
+ * `resetSync()` first if rows have already been retrieved, or this throws.
209
+ */
210
+ getAllSync(): T[];
211
+ /** Resets the prepared statement's cursor — calls `sqlite3_reset()` under the hood. */
212
+ resetSync(): void;
213
+ };
214
+
215
+ //#region Internals for ISQLiteExecuteAsyncResult and ISQLiteExecuteSyncResult
216
+
217
+ type ISQLiteExecuteResultOptions = {
218
+ rawResult: boolean;
219
+ lastInsertRowId: number;
220
+ changes: number;
221
+ };
222
+
223
+ /**
224
+ * Creates the `ISQLiteExecuteAsyncResult` — an async generator with the extra fields/methods
225
+ * attached via `Object.defineProperties`, since Hermes does not support `Symbol.asyncIterator`
226
+ * on a plain object literal.
227
+ */
228
+ async function createSQLiteExecuteAsyncResult<T>(
229
+ database: ISQLiteAnyDatabase,
230
+ statement: NativeStatement,
231
+ firstRowValues: ISQLiteColumnValues | null,
232
+ options: ISQLiteExecuteResultOptions,
233
+ ): Promise<ISQLiteExecuteAsyncResult<T>> {
234
+ const instance = new SQLiteExecuteAsyncResultImpl<T>(
235
+ database,
236
+ statement,
237
+ firstRowValues ? processNativeRow(firstRowValues) : null,
238
+ options,
239
+ );
240
+ const generator = instance.generatorAsync();
241
+ Object.defineProperties(generator, {
242
+ lastInsertRowId: {
243
+ value: options.lastInsertRowId,
244
+ enumerable: true,
245
+ writable: false,
246
+ configurable: true,
247
+ },
248
+ changes: {
249
+ value: options.changes,
250
+ enumerable: true,
251
+ writable: false,
252
+ configurable: true,
253
+ },
254
+ getFirstAsync: {
255
+ value: instance.getFirstAsync.bind(instance),
256
+ enumerable: true,
257
+ writable: false,
258
+ configurable: true,
259
+ },
260
+ getAllAsync: {
261
+ value: instance.getAllAsync.bind(instance),
262
+ enumerable: true,
263
+ writable: false,
264
+ configurable: true,
265
+ },
266
+ resetAsync: {
267
+ value: instance.resetAsync.bind(instance),
268
+ enumerable: true,
269
+ writable: false,
270
+ configurable: true,
271
+ },
272
+ });
273
+
274
+ // I/O edge: the generator is given the four extra members above at runtime via
275
+ // `Object.defineProperties` — nothing narrower can describe that to the type checker.
276
+ return generator as ISQLiteExecuteAsyncResult<T>;
277
+ }
278
+
279
+ /** Creates the `ISQLiteExecuteSyncResult`. */
280
+ function createSQLiteExecuteSyncResult<T>(
281
+ database: ISQLiteAnyDatabase,
282
+ statement: NativeStatement,
283
+ firstRowValues: ISQLiteColumnValues | null,
284
+ options: ISQLiteExecuteResultOptions,
285
+ ): ISQLiteExecuteSyncResult<T> {
286
+ const instance = new SQLiteExecuteSyncResultImpl<T>(
287
+ database,
288
+ statement,
289
+ firstRowValues ? processNativeRow(firstRowValues) : firstRowValues,
290
+ options,
291
+ );
292
+ const generator = instance.generatorSync();
293
+ Object.defineProperties(generator, {
294
+ lastInsertRowId: {
295
+ value: options.lastInsertRowId,
296
+ enumerable: true,
297
+ writable: false,
298
+ configurable: true,
299
+ },
300
+ changes: {
301
+ value: options.changes,
302
+ enumerable: true,
303
+ writable: false,
304
+ configurable: true,
305
+ },
306
+ getFirstSync: {
307
+ value: instance.getFirstSync.bind(instance),
308
+ enumerable: true,
309
+ writable: false,
310
+ configurable: true,
311
+ },
312
+ getAllSync: {
313
+ value: instance.getAllSync.bind(instance),
314
+ enumerable: true,
315
+ writable: false,
316
+ configurable: true,
317
+ },
318
+ resetSync: {
319
+ value: instance.resetSync.bind(instance),
320
+ enumerable: true,
321
+ writable: false,
322
+ configurable: true,
323
+ },
324
+ });
325
+
326
+ // I/O edge: see createSQLiteExecuteAsyncResult above.
327
+ return generator as ISQLiteExecuteSyncResult<T>;
328
+ }
329
+
330
+ class SQLiteExecuteAsyncResultImpl<T> {
331
+ private columnNames: string[] | null = null;
332
+ private isStepCalled = false;
333
+
334
+ constructor(
335
+ private readonly database: ISQLiteAnyDatabase,
336
+ private readonly statement: NativeStatement,
337
+ private firstRowValues: ISQLiteColumnValues | null,
338
+ public readonly options: ISQLiteExecuteResultOptions,
339
+ ) {}
340
+
341
+ async getFirstAsync(): Promise<T | null> {
342
+ if (this.isStepCalled) {
343
+ throw new Error(
344
+ 'The SQLite cursor has been shifted and is unable to retrieve the first row without being reset. Invoke `resetAsync()` to reset the cursor first if you want to retrieve the first row.',
345
+ );
346
+ }
347
+ this.isStepCalled = true;
348
+ const columnNames = await this.getColumnNamesAsync();
349
+ const firstRowValues = this.popFirstRowValues();
350
+ if (firstRowValues != null) {
351
+ return composeRowIfNeeded<T>(
352
+ this.options.rawResult,
353
+ columnNames,
354
+ firstRowValues,
355
+ );
356
+ }
357
+ const firstRow = await this.statement.stepAsync(this.database);
358
+ return firstRow != null
359
+ ? composeRowIfNeeded<T>(
360
+ this.options.rawResult,
361
+ columnNames,
362
+ processNativeRow(firstRow),
363
+ )
364
+ : null;
365
+ }
366
+
367
+ async getAllAsync(): Promise<T[]> {
368
+ if (this.isStepCalled) {
369
+ throw new Error(
370
+ 'The SQLite cursor has been shifted and is unable to retrieve all rows without being reset. Invoke `resetAsync()` to reset the cursor first if you want to retrieve all rows.',
371
+ );
372
+ }
373
+ this.isStepCalled = true;
374
+ const firstRowValues = this.popFirstRowValues();
375
+ if (firstRowValues == null) {
376
+ // An empty first row means this SQL was a write operation — calling getAllAsync() again
377
+ // would write a second time.
378
+ return [];
379
+ }
380
+ const columnNames = await this.getColumnNamesAsync();
381
+ const nativeRows = await this.statement.getAllAsync(this.database);
382
+ const allRows = processNativeRows(nativeRows);
383
+ if (firstRowValues.length > 0) {
384
+ return composeRowsIfNeeded<T>(this.options.rawResult, columnNames, [
385
+ firstRowValues,
386
+ ...allRows,
387
+ ]);
388
+ }
389
+ return composeRowsIfNeeded<T>(this.options.rawResult, columnNames, allRows);
390
+ }
391
+
392
+ async *generatorAsync(): AsyncIterableIterator<T> {
393
+ this.isStepCalled = true;
394
+ const columnNames = await this.getColumnNamesAsync();
395
+ const firstRowValues = this.popFirstRowValues();
396
+ if (firstRowValues != null) {
397
+ yield composeRowIfNeeded<T>(
398
+ this.options.rawResult,
399
+ columnNames,
400
+ firstRowValues,
401
+ );
402
+ }
403
+
404
+ let result;
405
+ do {
406
+ result = await this.statement.stepAsync(this.database);
407
+ if (result != null) {
408
+ yield composeRowIfNeeded<T>(
409
+ this.options.rawResult,
410
+ columnNames,
411
+ processNativeRow(result),
412
+ );
413
+ }
414
+ } while (result != null);
415
+ }
416
+
417
+ resetAsync(): Promise<void> {
418
+ const result = this.statement.resetAsync(this.database);
419
+ this.isStepCalled = false;
420
+ return result;
421
+ }
422
+
423
+ private popFirstRowValues(): ISQLiteColumnValues | null {
424
+ if (this.firstRowValues != null) {
425
+ const firstRowValues = this.firstRowValues;
426
+ this.firstRowValues = null;
427
+ return firstRowValues.length > 0 ? firstRowValues : null;
428
+ }
429
+ return null;
430
+ }
431
+
432
+ private async getColumnNamesAsync(): Promise<string[]> {
433
+ if (this.columnNames == null) {
434
+ this.columnNames = await this.statement.getColumnNamesAsync();
435
+ }
436
+ return this.columnNames;
437
+ }
438
+ }
439
+
440
+ class SQLiteExecuteSyncResultImpl<T> {
441
+ private columnNames: string[] | null = null;
442
+ private isStepCalled = false;
443
+
444
+ constructor(
445
+ private readonly database: ISQLiteAnyDatabase,
446
+ private readonly statement: NativeStatement,
447
+ private firstRowValues: ISQLiteColumnValues | null,
448
+ public readonly options: ISQLiteExecuteResultOptions,
449
+ ) {}
450
+
451
+ getFirstSync(): T | null {
452
+ if (this.isStepCalled) {
453
+ throw new Error(
454
+ 'The SQLite cursor has been shifted and is unable to retrieve the first row without being reset. Invoke `resetSync()` to reset the cursor first if you want to retrieve the first row.',
455
+ );
456
+ }
457
+ const columnNames = this.getColumnNamesSync();
458
+ const firstRowValues = this.popFirstRowValues();
459
+ if (firstRowValues != null) {
460
+ return composeRowIfNeeded<T>(
461
+ this.options.rawResult,
462
+ columnNames,
463
+ firstRowValues,
464
+ );
465
+ }
466
+ const firstRow = this.statement.stepSync(this.database);
467
+ return firstRow != null
468
+ ? composeRowIfNeeded<T>(
469
+ this.options.rawResult,
470
+ columnNames,
471
+ processNativeRow(firstRow),
472
+ )
473
+ : null;
474
+ }
475
+
476
+ getAllSync(): T[] {
477
+ if (this.isStepCalled) {
478
+ throw new Error(
479
+ 'The SQLite cursor has been shifted and is unable to retrieve all rows without being reset. Invoke `resetSync()` to reset the cursor first if you want to retrieve all rows.',
480
+ );
481
+ }
482
+ const firstRowValues = this.popFirstRowValues();
483
+ if (firstRowValues == null) {
484
+ return [];
485
+ }
486
+ const columnNames = this.getColumnNamesSync();
487
+ const nativeRows = this.statement.getAllSync(this.database);
488
+ const allRows = processNativeRows(nativeRows);
489
+ if (firstRowValues.length > 0) {
490
+ return composeRowsIfNeeded<T>(this.options.rawResult, columnNames, [
491
+ firstRowValues,
492
+ ...allRows,
493
+ ]);
494
+ }
495
+ return composeRowsIfNeeded<T>(this.options.rawResult, columnNames, allRows);
496
+ }
497
+
498
+ *generatorSync(): IterableIterator<T> {
499
+ const columnNames = this.getColumnNamesSync();
500
+ const firstRowValues = this.popFirstRowValues();
501
+ if (firstRowValues != null) {
502
+ yield composeRowIfNeeded<T>(
503
+ this.options.rawResult,
504
+ columnNames,
505
+ firstRowValues,
506
+ );
507
+ }
508
+ let result;
509
+ do {
510
+ result = this.statement.stepSync(this.database);
511
+ if (result != null) {
512
+ yield composeRowIfNeeded<T>(
513
+ this.options.rawResult,
514
+ columnNames,
515
+ processNativeRow(result),
516
+ );
517
+ }
518
+ } while (result != null);
519
+ }
520
+
521
+ resetSync(): void {
522
+ const result = this.statement.resetSync(this.database);
523
+ this.isStepCalled = false;
524
+ return result;
525
+ }
526
+
527
+ private popFirstRowValues(): ISQLiteColumnValues | null {
528
+ if (this.firstRowValues != null) {
529
+ const firstRowValues = this.firstRowValues;
530
+ this.firstRowValues = null;
531
+ return firstRowValues.length > 0 ? firstRowValues : null;
532
+ }
533
+ return null;
534
+ }
535
+
536
+ private getColumnNamesSync(): string[] {
537
+ if (this.columnNames == null) {
538
+ this.columnNames = this.statement.getColumnNamesSync();
539
+ }
540
+ return this.columnNames;
541
+ }
542
+ }
543
+
544
+ function composeRowIfNeeded<T>(
545
+ rawResult: boolean,
546
+ columnNames: ISQLiteColumnNames,
547
+ columnValues: ISQLiteColumnValues,
548
+ ): T {
549
+ // I/O edge: `rawResult` selects between two shapes of T (a row object, or `IValuesOf<T>`) that
550
+ // only the caller's own generic parameter distinguishes — see the two call sites above.
551
+ return rawResult
552
+ ? (columnValues as T)
553
+ : composeRow<T>(columnNames, columnValues);
554
+ }
555
+
556
+ function composeRowsIfNeeded<T>(
557
+ rawResult: boolean,
558
+ columnNames: ISQLiteColumnNames,
559
+ columnValuesList: ISQLiteColumnValues[],
560
+ ): T[] {
561
+ return rawResult
562
+ ? (columnValuesList as T[])
563
+ : composeRows<T>(columnNames, columnValuesList);
564
+ }
565
+
566
+ function processNativeRow(nativeRow: ISQLiteColumnValues): ISQLiteColumnValues {
567
+ return nativeRow.map(column =>
568
+ column instanceof ArrayBuffer ? new Uint8Array(column) : column,
569
+ );
570
+ }
571
+
572
+ function processNativeRows(
573
+ nativeRows: ISQLiteColumnValues[],
574
+ ): ISQLiteColumnValues[] {
575
+ return nativeRows.map(processNativeRow);
576
+ }
577
+
578
+ //#endregion