expo-sqlite 13.0.0 → 13.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 (61) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/cpp/NativeDatabaseBinding.cpp +4 -4
  4. package/android/src/main/cpp/NativeDatabaseBinding.h +2 -2
  5. package/android/src/main/cpp/NativeStatementBinding.cpp +6 -0
  6. package/android/src/main/cpp/NativeStatementBinding.h +1 -0
  7. package/android/src/main/java/expo/modules/sqlite/NativeDatabase.kt +1 -1
  8. package/android/src/main/java/expo/modules/sqlite/NativeDatabaseBinding.kt +4 -4
  9. package/android/src/main/java/expo/modules/sqlite/NativeStatementBinding.kt +5 -4
  10. package/android/src/main/java/expo/modules/sqlite/SQLiteModule.kt +21 -21
  11. package/android/src/main/java/expo/modules/sqlite/SQLiteModuleNext.kt +55 -68
  12. package/build/next/ExpoSQLiteNext.d.ts +4 -4
  13. package/build/next/ExpoSQLiteNext.d.ts.map +1 -1
  14. package/build/next/ExpoSQLiteNext.js +3 -3
  15. package/build/next/ExpoSQLiteNext.js.map +1 -1
  16. package/build/next/NativeDatabase.d.ts +3 -3
  17. package/build/next/NativeDatabase.d.ts.map +1 -1
  18. package/build/next/NativeDatabase.js.map +1 -1
  19. package/build/next/NativeStatement.d.ts +37 -31
  20. package/build/next/NativeStatement.d.ts.map +1 -1
  21. package/build/next/NativeStatement.js.map +1 -1
  22. package/build/next/SQLiteDatabase.d.ts +266 -0
  23. package/build/next/SQLiteDatabase.d.ts.map +1 -0
  24. package/build/next/{Database.js → SQLiteDatabase.js} +69 -59
  25. package/build/next/SQLiteDatabase.js.map +1 -0
  26. package/build/next/SQLiteStatement.d.ts +190 -0
  27. package/build/next/SQLiteStatement.d.ts.map +1 -0
  28. package/build/next/SQLiteStatement.js +275 -0
  29. package/build/next/SQLiteStatement.js.map +1 -0
  30. package/build/next/hooks.d.ts +26 -14
  31. package/build/next/hooks.d.ts.map +1 -1
  32. package/build/next/hooks.js +121 -33
  33. package/build/next/hooks.js.map +1 -1
  34. package/build/next/index.d.ts +2 -2
  35. package/build/next/index.d.ts.map +1 -1
  36. package/build/next/index.js +2 -2
  37. package/build/next/index.js.map +1 -1
  38. package/build/next/paramUtils.d.ts +18 -0
  39. package/build/next/paramUtils.d.ts.map +1 -0
  40. package/build/next/paramUtils.js +72 -0
  41. package/build/next/paramUtils.js.map +1 -0
  42. package/ios/NativeDatabase.swift +3 -3
  43. package/ios/SQLiteModule.swift +17 -17
  44. package/ios/SQLiteModuleNext.swift +56 -77
  45. package/package.json +4 -3
  46. package/src/next/ExpoSQLiteNext.ts +4 -4
  47. package/src/next/NativeDatabase.ts +3 -3
  48. package/src/next/NativeStatement.ts +39 -58
  49. package/src/next/{Database.ts → SQLiteDatabase.ts} +134 -112
  50. package/src/next/SQLiteStatement.ts +528 -0
  51. package/src/next/hooks.tsx +202 -51
  52. package/src/next/index.ts +2 -2
  53. package/src/next/paramUtils.ts +94 -0
  54. package/build/next/Database.d.ts +0 -272
  55. package/build/next/Database.d.ts.map +0 -1
  56. package/build/next/Database.js.map +0 -1
  57. package/build/next/Statement.d.ts +0 -140
  58. package/build/next/Statement.d.ts.map +0 -1
  59. package/build/next/Statement.js +0 -173
  60. package/build/next/Statement.js.map +0 -1
  61. package/src/next/Statement.ts +0 -315
@@ -0,0 +1,275 @@
1
+ import { composeRow, composeRows, normalizeParams } from './paramUtils';
2
+ /**
3
+ * A prepared statement returned by [`SQLiteDatabase.prepareAsync()`](#prepareasyncsource) or [`SQLiteDatabase.prepareSync()`](#preparesyncsource) that can be binded with parameters and executed.
4
+ */
5
+ export class SQLiteStatement {
6
+ nativeDatabase;
7
+ nativeStatement;
8
+ constructor(nativeDatabase, nativeStatement) {
9
+ this.nativeDatabase = nativeDatabase;
10
+ this.nativeStatement = nativeStatement;
11
+ }
12
+ async executeAsync(...params) {
13
+ const { lastInsertRowId, changes, firstRowValues } = await this.nativeStatement.runAsync(this.nativeDatabase, ...normalizeParams(...params));
14
+ return createSQLiteExecuteAsyncResult(this.nativeDatabase, this.nativeStatement, lastInsertRowId, changes, firstRowValues);
15
+ }
16
+ /**
17
+ * Get the column names of the prepared statement.
18
+ */
19
+ getColumnNamesAsync() {
20
+ return this.nativeStatement.getColumnNamesAsync();
21
+ }
22
+ /**
23
+ * Finalize the prepared statement. This will call the [`sqlite3_finalize()`](https://www.sqlite.org/c3ref/finalize.html) C function under the hood.
24
+ *
25
+ * Attempting to access a finalized statement will result in an error.
26
+ * > **Note:** While expo-sqlite will automatically finalize any orphaned prepared statements upon closing the database, it is considered best practice to manually finalize prepared statements as soon as they are no longer needed. This helps to prevent resource leaks. You can use the `try...finally` statement to ensure that prepared statements are finalized even if an error occurs.
27
+ */
28
+ async finalizeAsync() {
29
+ await this.nativeStatement.finalizeAsync(this.nativeDatabase);
30
+ }
31
+ executeSync(...params) {
32
+ const { lastInsertRowId, changes, firstRowValues } = this.nativeStatement.runSync(this.nativeDatabase, ...normalizeParams(...params));
33
+ return createSQLiteExecuteSyncResult(this.nativeDatabase, this.nativeStatement, lastInsertRowId, changes, firstRowValues);
34
+ }
35
+ /**
36
+ * Get the column names of the prepared statement.
37
+ */
38
+ getColumnNamesSync() {
39
+ return this.nativeStatement.getColumnNamesSync();
40
+ }
41
+ /**
42
+ * Finalize the prepared statement. This will call the [`sqlite3_finalize()`](https://www.sqlite.org/c3ref/finalize.html) C function under the hood.
43
+ *
44
+ * Attempting to access a finalized statement will result in an error.
45
+ * > **Note:** While expo-sqlite will automatically finalize any orphaned prepared statements upon closing the database, it is considered best practice to manually finalize prepared statements as soon as they are no longer needed. This helps to prevent resource leaks. You can use the `try...finally` statement to ensure that prepared statements are finalized even if an error occurs.
46
+ */
47
+ finalizeSync() {
48
+ this.nativeStatement.finalizeSync(this.nativeDatabase);
49
+ }
50
+ }
51
+ //#region Internals for SQLiteExecuteAsyncResult and SQLiteExecuteSyncResult
52
+ /**
53
+ * Create the `SQLiteExecuteAsyncResult` instance.
54
+ *
55
+ * NOTE: Since Hermes does not support the `Symbol.asyncIterator` feature, we have to use an AsyncGenerator to implement the `AsyncIterableIterator` interface.
56
+ * This is done by `Object.defineProperties` to add the properties to the AsyncGenerator.
57
+ */
58
+ async function createSQLiteExecuteAsyncResult(database, statement, lastInsertRowId, changes, firstRowValues) {
59
+ const instance = new SQLiteExecuteAsyncResultImpl(database, statement, lastInsertRowId, changes, firstRowValues);
60
+ const generator = instance.generatorAsync();
61
+ Object.defineProperties(generator, {
62
+ lastInsertRowId: {
63
+ value: lastInsertRowId,
64
+ enumerable: true,
65
+ writable: false,
66
+ configurable: true,
67
+ },
68
+ changes: { value: changes, enumerable: true, writable: false, configurable: true },
69
+ getFirstAsync: {
70
+ value: instance.getFirstAsync.bind(instance),
71
+ enumerable: true,
72
+ writable: false,
73
+ configurable: true,
74
+ },
75
+ getAllAsync: {
76
+ value: instance.getAllAsync.bind(instance),
77
+ enumerable: true,
78
+ writable: false,
79
+ configurable: true,
80
+ },
81
+ resetAsync: {
82
+ value: instance.resetAsync.bind(instance),
83
+ enumerable: true,
84
+ writable: false,
85
+ configurable: true,
86
+ },
87
+ });
88
+ return generator;
89
+ }
90
+ /**
91
+ * Create the `SQLiteExecuteSyncResult` instance.
92
+ */
93
+ function createSQLiteExecuteSyncResult(database, statement, lastInsertRowId, changes, firstRowValues) {
94
+ const instance = new SQLiteExecuteSyncResultImpl(database, statement, lastInsertRowId, changes, firstRowValues);
95
+ const generator = instance.generatorSync();
96
+ Object.defineProperties(generator, {
97
+ lastInsertRowId: {
98
+ value: lastInsertRowId,
99
+ enumerable: true,
100
+ writable: false,
101
+ configurable: true,
102
+ },
103
+ changes: { value: changes, enumerable: true, writable: false, configurable: true },
104
+ getFirstSync: {
105
+ value: instance.getFirstSync.bind(instance),
106
+ enumerable: true,
107
+ writable: false,
108
+ configurable: true,
109
+ },
110
+ getAllSync: {
111
+ value: instance.getAllSync.bind(instance),
112
+ enumerable: true,
113
+ writable: false,
114
+ configurable: true,
115
+ },
116
+ resetSync: {
117
+ value: instance.resetSync.bind(instance),
118
+ enumerable: true,
119
+ writable: false,
120
+ configurable: true,
121
+ },
122
+ });
123
+ return generator;
124
+ }
125
+ class SQLiteExecuteAsyncResultImpl {
126
+ database;
127
+ statement;
128
+ lastInsertRowId;
129
+ changes;
130
+ firstRowValues;
131
+ columnNames = null;
132
+ isStepCalled = false;
133
+ constructor(database, statement, lastInsertRowId, changes, firstRowValues) {
134
+ this.database = database;
135
+ this.statement = statement;
136
+ this.lastInsertRowId = lastInsertRowId;
137
+ this.changes = changes;
138
+ this.firstRowValues = firstRowValues;
139
+ }
140
+ async getFirstAsync() {
141
+ if (this.isStepCalled) {
142
+ throw new Error('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.');
143
+ }
144
+ this.isStepCalled = true;
145
+ const columnNames = await this.getColumnNamesAsync();
146
+ const firstRowValues = this.popFirstRowValues();
147
+ if (firstRowValues != null) {
148
+ return composeRow(columnNames, firstRowValues);
149
+ }
150
+ const firstRow = await this.statement.stepAsync(this.database);
151
+ return firstRow != null ? composeRow(columnNames, firstRow) : null;
152
+ }
153
+ async getAllAsync() {
154
+ if (this.isStepCalled) {
155
+ throw new Error('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.');
156
+ }
157
+ this.isStepCalled = true;
158
+ const columnNames = await this.getColumnNamesAsync();
159
+ const allRows = await this.statement.getAllAsync(this.database);
160
+ const firstRowValues = this.popFirstRowValues();
161
+ if (firstRowValues != null && firstRowValues.length > 0) {
162
+ return composeRows(columnNames, [firstRowValues, ...allRows]);
163
+ }
164
+ return composeRows(columnNames, allRows);
165
+ }
166
+ async *generatorAsync() {
167
+ this.isStepCalled = true;
168
+ const columnNames = await this.getColumnNamesAsync();
169
+ const firstRowValues = this.popFirstRowValues();
170
+ if (firstRowValues != null) {
171
+ yield composeRow(columnNames, firstRowValues);
172
+ }
173
+ let result;
174
+ do {
175
+ result = await this.statement.stepAsync(this.database);
176
+ if (result != null) {
177
+ yield composeRow(columnNames, result);
178
+ }
179
+ } while (result != null);
180
+ }
181
+ resetAsync() {
182
+ const result = this.statement.resetAsync(this.database);
183
+ this.isStepCalled = false;
184
+ return result;
185
+ }
186
+ popFirstRowValues() {
187
+ if (this.firstRowValues != null) {
188
+ const firstRowValues = this.firstRowValues;
189
+ this.firstRowValues = null;
190
+ return firstRowValues.length > 0 ? firstRowValues : null;
191
+ }
192
+ return null;
193
+ }
194
+ async getColumnNamesAsync() {
195
+ if (this.columnNames == null) {
196
+ this.columnNames = await this.statement.getColumnNamesAsync();
197
+ }
198
+ return this.columnNames;
199
+ }
200
+ }
201
+ class SQLiteExecuteSyncResultImpl {
202
+ database;
203
+ statement;
204
+ lastInsertRowId;
205
+ changes;
206
+ firstRowValues;
207
+ columnNames = null;
208
+ isStepCalled = false;
209
+ constructor(database, statement, lastInsertRowId, changes, firstRowValues) {
210
+ this.database = database;
211
+ this.statement = statement;
212
+ this.lastInsertRowId = lastInsertRowId;
213
+ this.changes = changes;
214
+ this.firstRowValues = firstRowValues;
215
+ }
216
+ getFirstSync() {
217
+ if (this.isStepCalled) {
218
+ throw new Error('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.');
219
+ }
220
+ const columnNames = this.getColumnNamesSync();
221
+ const firstRowValues = this.popFirstRowValues();
222
+ if (firstRowValues != null) {
223
+ return composeRow(columnNames, firstRowValues);
224
+ }
225
+ const firstRow = this.statement.stepSync(this.database);
226
+ return firstRow != null ? composeRow(columnNames, firstRow) : null;
227
+ }
228
+ getAllSync() {
229
+ if (this.isStepCalled) {
230
+ throw new Error('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.');
231
+ }
232
+ const columnNames = this.getColumnNamesSync();
233
+ const allRows = this.statement.getAllSync(this.database);
234
+ const firstRowValues = this.popFirstRowValues();
235
+ if (firstRowValues != null && firstRowValues.length > 0) {
236
+ return composeRows(columnNames, [firstRowValues, ...allRows]);
237
+ }
238
+ return composeRows(columnNames, allRows);
239
+ }
240
+ *generatorSync() {
241
+ const columnNames = this.getColumnNamesSync();
242
+ const firstRowValues = this.popFirstRowValues();
243
+ if (firstRowValues != null) {
244
+ yield composeRow(columnNames, firstRowValues);
245
+ }
246
+ let result;
247
+ do {
248
+ result = this.statement.stepSync(this.database);
249
+ if (result != null) {
250
+ yield composeRow(columnNames, result);
251
+ }
252
+ } while (result != null);
253
+ }
254
+ resetSync() {
255
+ const result = this.statement.resetSync(this.database);
256
+ this.isStepCalled = false;
257
+ return result;
258
+ }
259
+ popFirstRowValues() {
260
+ if (this.firstRowValues != null) {
261
+ const firstRowValues = this.firstRowValues;
262
+ this.firstRowValues = null;
263
+ return firstRowValues.length > 0 ? firstRowValues : null;
264
+ }
265
+ return null;
266
+ }
267
+ getColumnNamesSync() {
268
+ if (this.columnNames == null) {
269
+ this.columnNames = this.statement.getColumnNamesSync();
270
+ }
271
+ return this.columnNames;
272
+ }
273
+ }
274
+ //#endregion
275
+ //# sourceMappingURL=SQLiteStatement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SQLiteStatement.js","sourceRoot":"","sources":["../../src/next/SQLiteStatement.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeDatabase } from './NativeDatabase';\nimport {\n SQLiteBindParams,\n SQLiteBindValue,\n NativeStatement,\n SQLiteVariadicBindParams,\n type SQLiteAnyDatabase,\n type SQLiteRunResult,\n SQLiteColumnValues,\n} from './NativeStatement';\nimport { composeRow, composeRows, normalizeParams } from './paramUtils';\n\nexport { SQLiteBindParams, SQLiteBindValue, SQLiteRunResult, SQLiteVariadicBindParams };\n\n/**\n * A prepared statement returned by [`SQLiteDatabase.prepareAsync()`](#prepareasyncsource) or [`SQLiteDatabase.prepareSync()`](#preparesyncsource) that can be binded with parameters and executed.\n */\nexport class SQLiteStatement {\n constructor(\n private readonly nativeDatabase: NativeDatabase,\n private readonly nativeStatement: NativeStatement\n ) {}\n\n //#region Asynchronous API\n\n /**\n * Run the prepared statement and return the [`SQLiteExecuteAsyncResult`](#sqliteexecuteasyncresult) instance.\n * @param params The parameters to bind to the prepared statement. You can pass values in array, object, or variadic arguments. See [`SQLiteBindValue`](#sqlitebindvalue) for more information about binding values.\n */\n public executeAsync<T>(params: SQLiteBindParams): Promise<SQLiteExecuteAsyncResult<T>>;\n /**\n * @hidden\n */\n public executeAsync<T>(...params: SQLiteVariadicBindParams): Promise<SQLiteExecuteAsyncResult<T>>;\n public async executeAsync<T>(...params: unknown[]): Promise<SQLiteExecuteAsyncResult<T>> {\n const { lastInsertRowId, changes, firstRowValues } = await this.nativeStatement.runAsync(\n this.nativeDatabase,\n ...normalizeParams(...params)\n );\n return createSQLiteExecuteAsyncResult<T>(\n this.nativeDatabase,\n this.nativeStatement,\n lastInsertRowId,\n changes,\n firstRowValues\n );\n }\n\n /**\n * Get the column names of the prepared statement.\n */\n public getColumnNamesAsync(): Promise<string[]> {\n return this.nativeStatement.getColumnNamesAsync();\n }\n\n /**\n * Finalize the prepared statement. This will call the [`sqlite3_finalize()`](https://www.sqlite.org/c3ref/finalize.html) C function under the hood.\n *\n * Attempting to access a finalized statement will result in an error.\n * > **Note:** While expo-sqlite will automatically finalize any orphaned prepared statements upon closing the database, it is considered best practice to manually finalize prepared statements as soon as they are no longer needed. This helps to prevent resource leaks. You can use the `try...finally` statement to ensure that prepared statements are finalized even if an error occurs.\n */\n public async finalizeAsync(): Promise<void> {\n await this.nativeStatement.finalizeAsync(this.nativeDatabase);\n }\n\n //#endregion\n\n //#region Synchronous API\n\n /**\n * Run the prepared statement and return the [`SQLiteExecuteSyncResult`](#sqliteexecutesyncresult) instance.\n * > **Note:** Running heavy tasks with this function can block the JavaScript thread and affect performance.\n * @param params The parameters to bind to the prepared statement. You can pass values in array, object, or variadic arguments. See [`SQLiteBindValue`](#sqlitebindvalue) for more information about binding values.\n */\n public executeSync<T>(params: SQLiteBindParams): SQLiteExecuteSyncResult<T>;\n /**\n * @hidden\n */\n public executeSync<T>(...params: SQLiteVariadicBindParams): SQLiteExecuteSyncResult<T>;\n public executeSync<T>(...params: unknown[]): SQLiteExecuteSyncResult<T> {\n const { lastInsertRowId, changes, firstRowValues } = this.nativeStatement.runSync(\n this.nativeDatabase,\n ...normalizeParams(...params)\n );\n return createSQLiteExecuteSyncResult<T>(\n this.nativeDatabase,\n this.nativeStatement,\n lastInsertRowId,\n changes,\n firstRowValues\n );\n }\n\n /**\n * Get the column names of the prepared statement.\n */\n public getColumnNamesSync(): string[] {\n return this.nativeStatement.getColumnNamesSync();\n }\n\n /**\n * Finalize the prepared statement. This will call the [`sqlite3_finalize()`](https://www.sqlite.org/c3ref/finalize.html) C function under the hood.\n *\n * Attempting to access a finalized statement will result in an error.\n * > **Note:** While expo-sqlite will automatically finalize any orphaned prepared statements upon closing the database, it is considered best practice to manually finalize prepared statements as soon as they are no longer needed. This helps to prevent resource leaks. You can use the `try...finally` statement to ensure that prepared statements are finalized even if an error occurs.\n */\n public finalizeSync(): void {\n this.nativeStatement.finalizeSync(this.nativeDatabase);\n }\n\n //#endregion\n}\n\n/**\n * A result returned by [`SQLiteStatement.executeAsync()`](#executeasyncparams).\n *\n * @example\n * The result includes the [`lastInsertRowId`](https://www.sqlite.org/c3ref/last_insert_rowid.html) and [`changes`](https://www.sqlite.org/c3ref/changes.html) properties. You can get the information from the write operations.\n * ```ts\n * const statement = await db.prepareAsync('INSERT INTO test (value) VALUES (?)');\n * try {\n * const result = await statement.executeAsync(101);\n * console.log('lastInsertRowId:', result.lastInsertRowId);\n * console.log('changes:', result.changes);\n * } finally {\n * await statement.finalizeAsync();\n * }\n * ```\n *\n * @example\n * The result implements the [`AsyncIterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator) interface, so you can use it in `for await...of` loops.\n * ```ts\n * const statement = await db.prepareAsync('SELECT value FROM test WHERE value > ?');\n * try {\n * const result = await statement.executeAsync<{ value: number }>(100);\n * for await (const row of result) {\n * console.log('row value:', row.value);\n * }\n * } finally {\n * await statement.finalizeAsync();\n * }\n * ```\n *\n * @example\n * If your write operations also return values, you can mix all of them together.\n * ```ts\n * const statement = await db.prepareAsync('INSERT INTO test (name, value) VALUES (?, ?) RETURNING name');\n * try {\n * const result = await statement.executeAsync<{ name: string }>('John Doe', 101);\n * console.log('lastInsertRowId:', result.lastInsertRowId);\n * console.log('changes:', result.changes);\n * for await (const row of result) {\n * console.log('name:', row.name);\n * }\n * } finally {\n * await statement.finalizeAsync();\n * }\n * ```\n */\nexport interface SQLiteExecuteAsyncResult<T> extends AsyncIterableIterator<T> {\n /**\n * The last inserted row ID. Returned from the [`sqlite3_last_insert_rowid()`](https://www.sqlite.org/c3ref/last_insert_rowid.html) function.\n */\n readonly lastInsertRowId: number;\n\n /**\n * The number of rows affected. Returned from the [`sqlite3_changes()`](https://www.sqlite.org/c3ref/changes.html) function.\n */\n readonly changes: number;\n\n /**\n * Get the first row of the result set. This requires the SQLite cursor to be in its initial state. If you have already retrieved rows from the result set, you need to reset the cursor first by calling [`resetAsync()`](#resetasync). Otherwise, an error will be thrown.\n */\n getFirstAsync(): Promise<T | null>;\n\n /**\n * Get all rows of the result set. This requires the SQLite cursor to be in its initial state. If you have already retrieved rows from the result set, you need to reset the cursor first by calling [`resetAsync()`](#resetasync). Otherwise, an error will be thrown.\n */\n getAllAsync(): Promise<T[]>;\n\n /**\n * Reset the prepared statement cursor. This will call the [`sqlite3_reset()`](https://www.sqlite.org/c3ref/reset.html) C function under the hood.\n */\n resetAsync(): Promise<void>;\n}\n\n/**\n * A result returned by [`SQLiteStatement.executeSync()`](#executesyncparams).\n * > **Note:** Running heavy tasks with this function can block the JavaScript thread and affect performance.\n\n * @example\n * The result includes the [`lastInsertRowId`](https://www.sqlite.org/c3ref/last_insert_rowid.html) and [`changes`](https://www.sqlite.org/c3ref/changes.html) properties. You can get the information from the write operations.\n * ```ts\n * const statement = db.prepareSync('INSERT INTO test (value) VALUES (?)');\n * try {\n * const result = statement.executeSync(101);\n * console.log('lastInsertRowId:', result.lastInsertRowId);\n * console.log('changes:', result.changes);\n * } finally {\n * statement.finalizeSync();\n * }\n * ```\n *\n * @example\n * The result implements the [`Iterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator) interface, so you can use it in `for...of` loops.\n * ```ts\n * const statement = db.prepareSync('SELECT value FROM test WHERE value > ?');\n * try {\n * const result = statement.executeSync<{ value: number }>(100);\n * for (const row of result) {\n * console.log('row value:', row.value);\n * }\n * } finally {\n * statement.finalizeSync();\n * }\n * ```\n *\n * @example\n * If your write operations also return values, you can mix all of them together.\n * ```ts\n * const statement = db.prepareSync('INSERT INTO test (name, value) VALUES (?, ?) RETURNING name');\n * try {\n * const result = statement.executeSync<{ name: string }>('John Doe', 101);\n * console.log('lastInsertRowId:', result.lastInsertRowId);\n * console.log('changes:', result.changes);\n * for (const row of result) {\n * console.log('name:', row.name);\n * }\n * } finally {\n * statement.finalizeSync();\n * }\n * ```\n */\nexport interface SQLiteExecuteSyncResult<T> extends IterableIterator<T> {\n /**\n * The last inserted row ID. Returned from the [`sqlite3_last_insert_rowid()`](https://www.sqlite.org/c3ref/last_insert_rowid.html) function.\n */\n readonly lastInsertRowId: number;\n\n /**\n * The number of rows affected. Returned from the [`sqlite3_changes()`](https://www.sqlite.org/c3ref/changes.html) function.\n */\n readonly changes: number;\n\n /**\n * Get the first row of the result set. This requires the SQLite cursor to be in its initial state. If you have already retrieved rows from the result set, you need to reset the cursor first by calling [`resetSync()`](#resetsync). Otherwise, an error will be thrown.\n */\n getFirstSync(): T | null;\n\n /**\n * Get all rows of the result set. This requires the SQLite cursor to be in its initial state. If you have already retrieved rows from the result set, you need to reset the cursor first by calling [`resetSync()`](#resetsync). Otherwise, an error will be thrown.\n */\n getAllSync(): T[];\n\n /**\n * Reset the prepared statement cursor. This will call the [`sqlite3_reset()`](https://www.sqlite.org/c3ref/reset.html) C function under the hood.\n */\n resetSync(): void;\n}\n\n//#region Internals for SQLiteExecuteAsyncResult and SQLiteExecuteSyncResult\n\n/**\n * Create the `SQLiteExecuteAsyncResult` instance.\n *\n * NOTE: Since Hermes does not support the `Symbol.asyncIterator` feature, we have to use an AsyncGenerator to implement the `AsyncIterableIterator` interface.\n * This is done by `Object.defineProperties` to add the properties to the AsyncGenerator.\n */\nasync function createSQLiteExecuteAsyncResult<T>(\n database: SQLiteAnyDatabase,\n statement: NativeStatement,\n lastInsertRowId: number,\n changes: number,\n firstRowValues: SQLiteColumnValues | null\n): Promise<SQLiteExecuteAsyncResult<T>> {\n const instance = new SQLiteExecuteAsyncResultImpl<T>(\n database,\n statement,\n lastInsertRowId,\n changes,\n firstRowValues\n );\n const generator = instance.generatorAsync();\n Object.defineProperties(generator, {\n lastInsertRowId: {\n value: lastInsertRowId,\n enumerable: true,\n writable: false,\n configurable: true,\n },\n changes: { value: changes, enumerable: true, writable: false, configurable: true },\n getFirstAsync: {\n value: instance.getFirstAsync.bind(instance),\n enumerable: true,\n writable: false,\n configurable: true,\n },\n getAllAsync: {\n value: instance.getAllAsync.bind(instance),\n enumerable: true,\n writable: false,\n configurable: true,\n },\n resetAsync: {\n value: instance.resetAsync.bind(instance),\n enumerable: true,\n writable: false,\n configurable: true,\n },\n });\n\n return generator as SQLiteExecuteAsyncResult<T>;\n}\n\n/**\n * Create the `SQLiteExecuteSyncResult` instance.\n */\nfunction createSQLiteExecuteSyncResult<T>(\n database: SQLiteAnyDatabase,\n statement: NativeStatement,\n lastInsertRowId: number,\n changes: number,\n firstRowValues: SQLiteColumnValues | null\n): SQLiteExecuteSyncResult<T> {\n const instance = new SQLiteExecuteSyncResultImpl<T>(\n database,\n statement,\n lastInsertRowId,\n changes,\n firstRowValues\n );\n const generator = instance.generatorSync();\n Object.defineProperties(generator, {\n lastInsertRowId: {\n value: lastInsertRowId,\n enumerable: true,\n writable: false,\n configurable: true,\n },\n changes: { value: changes, enumerable: true, writable: false, configurable: true },\n getFirstSync: {\n value: instance.getFirstSync.bind(instance),\n enumerable: true,\n writable: false,\n configurable: true,\n },\n getAllSync: {\n value: instance.getAllSync.bind(instance),\n enumerable: true,\n writable: false,\n configurable: true,\n },\n resetSync: {\n value: instance.resetSync.bind(instance),\n enumerable: true,\n writable: false,\n configurable: true,\n },\n });\n\n return generator as SQLiteExecuteSyncResult<T>;\n}\n\nclass SQLiteExecuteAsyncResultImpl<T> {\n private columnNames: string[] | null = null;\n private isStepCalled = false;\n\n constructor(\n private readonly database: SQLiteAnyDatabase,\n private readonly statement: NativeStatement,\n public readonly lastInsertRowId: number,\n public readonly changes: number,\n private firstRowValues: SQLiteColumnValues | null\n ) {}\n\n async getFirstAsync(): Promise<T | null> {\n if (this.isStepCalled) {\n throw new Error(\n '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.'\n );\n }\n this.isStepCalled = true;\n const columnNames = await this.getColumnNamesAsync();\n const firstRowValues = this.popFirstRowValues();\n if (firstRowValues != null) {\n return composeRow<T>(columnNames, firstRowValues);\n }\n const firstRow = await this.statement.stepAsync(this.database);\n return firstRow != null ? composeRow<T>(columnNames, firstRow) : null;\n }\n\n async getAllAsync(): Promise<T[]> {\n if (this.isStepCalled) {\n throw new Error(\n '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.'\n );\n }\n this.isStepCalled = true;\n const columnNames = await this.getColumnNamesAsync();\n const allRows = await this.statement.getAllAsync(this.database);\n const firstRowValues = this.popFirstRowValues();\n if (firstRowValues != null && firstRowValues.length > 0) {\n return composeRows<T>(columnNames, [firstRowValues, ...allRows]);\n }\n return composeRows<T>(columnNames, allRows);\n }\n\n async *generatorAsync(): AsyncIterableIterator<T> {\n this.isStepCalled = true;\n const columnNames = await this.getColumnNamesAsync();\n const firstRowValues = this.popFirstRowValues();\n if (firstRowValues != null) {\n yield composeRow<T>(columnNames, firstRowValues);\n }\n\n let result;\n do {\n result = await this.statement.stepAsync(this.database);\n if (result != null) {\n yield composeRow<T>(columnNames, result);\n }\n } while (result != null);\n }\n\n resetAsync(): Promise<void> {\n const result = this.statement.resetAsync(this.database);\n this.isStepCalled = false;\n return result;\n }\n\n private popFirstRowValues(): SQLiteColumnValues | null {\n if (this.firstRowValues != null) {\n const firstRowValues = this.firstRowValues;\n this.firstRowValues = null;\n return firstRowValues.length > 0 ? firstRowValues : null;\n }\n return null;\n }\n\n private async getColumnNamesAsync(): Promise<string[]> {\n if (this.columnNames == null) {\n this.columnNames = await this.statement.getColumnNamesAsync();\n }\n return this.columnNames;\n }\n}\n\nclass SQLiteExecuteSyncResultImpl<T> {\n private columnNames: string[] | null = null;\n private isStepCalled = false;\n\n constructor(\n private readonly database: SQLiteAnyDatabase,\n private readonly statement: NativeStatement,\n public readonly lastInsertRowId: number,\n public readonly changes: number,\n private firstRowValues: SQLiteColumnValues | null\n ) {}\n\n getFirstSync(): T | null {\n if (this.isStepCalled) {\n throw new Error(\n '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.'\n );\n }\n const columnNames = this.getColumnNamesSync();\n const firstRowValues = this.popFirstRowValues();\n if (firstRowValues != null) {\n return composeRow<T>(columnNames, firstRowValues);\n }\n const firstRow = this.statement.stepSync(this.database);\n return firstRow != null ? composeRow<T>(columnNames, firstRow) : null;\n }\n\n getAllSync(): T[] {\n if (this.isStepCalled) {\n throw new Error(\n '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.'\n );\n }\n const columnNames = this.getColumnNamesSync();\n const allRows = this.statement.getAllSync(this.database);\n const firstRowValues = this.popFirstRowValues();\n if (firstRowValues != null && firstRowValues.length > 0) {\n return composeRows<T>(columnNames, [firstRowValues, ...allRows]);\n }\n return composeRows<T>(columnNames, allRows);\n }\n\n *generatorSync(): IterableIterator<T> {\n const columnNames = this.getColumnNamesSync();\n const firstRowValues = this.popFirstRowValues();\n if (firstRowValues != null) {\n yield composeRow<T>(columnNames, firstRowValues);\n }\n let result;\n do {\n result = this.statement.stepSync(this.database);\n if (result != null) {\n yield composeRow<T>(columnNames, result);\n }\n } while (result != null);\n }\n\n resetSync(): void {\n const result = this.statement.resetSync(this.database);\n this.isStepCalled = false;\n return result;\n }\n\n private popFirstRowValues(): SQLiteColumnValues | null {\n if (this.firstRowValues != null) {\n const firstRowValues = this.firstRowValues;\n this.firstRowValues = null;\n return firstRowValues.length > 0 ? firstRowValues : null;\n }\n return null;\n }\n\n private getColumnNamesSync(): string[] {\n if (this.columnNames == null) {\n this.columnNames = this.statement.getColumnNamesSync();\n }\n return this.columnNames;\n }\n}\n\n//#endregion\n"]}
@@ -1,15 +1,15 @@
1
1
  import React from 'react';
2
- import { type Database } from './Database';
3
- import type { OpenOptions } from './NativeDatabase';
2
+ import type { SQLiteOpenOptions } from './NativeDatabase';
3
+ import { type SQLiteDatabase } from './SQLiteDatabase';
4
4
  export interface SQLiteProviderProps {
5
5
  /**
6
6
  * The name of the database file to open.
7
7
  */
8
- dbName: string;
8
+ databaseName: string;
9
9
  /**
10
10
  * Open options.
11
11
  */
12
- options?: OpenOptions;
12
+ options?: SQLiteOpenOptions;
13
13
  /**
14
14
  * The children to render.
15
15
  */
@@ -18,23 +18,35 @@ export interface SQLiteProviderProps {
18
18
  * A custom initialization handler to run before rendering the children.
19
19
  * You can use this to run database migrations or other setup tasks.
20
20
  */
21
- initHandler?: (db: Database) => Promise<void>;
22
- /**
23
- * A custom loading fallback to render before the database is ready.
24
- * @default null
25
- */
26
- loadingFallback?: React.ReactNode;
21
+ onInit?: (db: SQLiteDatabase) => Promise<void>;
27
22
  /**
28
23
  * Handle errors from SQLiteProvider.
29
24
  * @default rethrow the error
30
25
  */
31
- errorHandler?: (error: Error) => void;
26
+ onError?: (error: Error) => void;
27
+ /**
28
+ * Enable [`React.Suspense`](https://react.dev/reference/react/Suspense) integration.
29
+ * @default false
30
+ * @example
31
+ * ```tsx
32
+ * export default function App() {
33
+ * return (
34
+ * <Suspense fallback={<Text>Loading...</Text>}>
35
+ * <SQLiteProvider databaseName="test.db" useSuspense={true}>
36
+ * <Main />
37
+ * </SQLiteProvider>
38
+ * </Suspense>
39
+ * );
40
+ * }
41
+ * ```
42
+ */
43
+ useSuspense?: boolean;
32
44
  }
33
45
  /**
34
46
  * Context.Provider component that provides a SQLite database to all children.
35
47
  * All descendants of this component will be able to access the database using the [`useSQLiteContext`](#usesqlitecontext) hook.
36
48
  */
37
- export declare function SQLiteProvider({ dbName, options, children, initHandler, loadingFallback, errorHandler, }: SQLiteProviderProps): JSX.Element | null;
49
+ export declare function SQLiteProvider({ children, onError, useSuspense, ...props }: SQLiteProviderProps): JSX.Element;
38
50
  /**
39
51
  * A global hook for accessing the SQLite database across components.
40
52
  * This hook should only be used within a [`<SQLiteProvider>`](#sqliteprovider) component.
@@ -43,7 +55,7 @@ export declare function SQLiteProvider({ dbName, options, children, initHandler,
43
55
  * ```tsx
44
56
  * export default function App() {
45
57
  * return (
46
- * <SQLiteProvider dbName="test.db">
58
+ * <SQLiteProvider databaseName="test.db">
47
59
  * <Main />
48
60
  * </SQLiteProvider>
49
61
  * );
@@ -56,5 +68,5 @@ export declare function SQLiteProvider({ dbName, options, children, initHandler,
56
68
  * }
57
69
  * ```
58
70
  */
59
- export declare function useSQLiteContext(): Database;
71
+ export declare function useSQLiteContext(): SQLiteDatabase;
60
72
  //# sourceMappingURL=hooks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/next/hooks.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAiE,MAAM,OAAO,CAAC;AAEtF,OAAO,EAAqB,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,OAAO,CAAC,EAAE,WAAW,CAAC;IAEtB;;OAEG;IACH,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAE1B;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9C;;;OAGG;IACH,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAElC;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACvC;AAOD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,OAAO,EACP,QAAQ,EACR,WAAW,EACX,eAAe,EACf,YAAY,GACb,EAAE,mBAAmB,sBAkDrB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,IAAI,QAAQ,CAM3C"}
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/next/hooks.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAiE,MAAM,OAAO,CAAC;AAEtF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAE1E,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAE5B;;OAEG;IACH,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAE1B;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/C;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC;;;;;;;;;;;;;;;OAeG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAOD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,OAAO,EACP,WAAmB,EACnB,GAAG,KAAK,EACT,EAAE,mBAAmB,eAcrB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,IAAI,cAAc,CAMjD"}
@@ -1,5 +1,5 @@
1
1
  import React, { createContext, useContext, useEffect, useRef, useState } from 'react';
2
- import { openDatabaseAsync } from './Database';
2
+ import { openDatabaseAsync } from './SQLiteDatabase';
3
3
  /**
4
4
  * Create a context for the SQLite database
5
5
  */
@@ -8,17 +8,63 @@ const SQLiteContext = createContext(null);
8
8
  * Context.Provider component that provides a SQLite database to all children.
9
9
  * All descendants of this component will be able to access the database using the [`useSQLiteContext`](#usesqlitecontext) hook.
10
10
  */
11
- export function SQLiteProvider({ dbName, options, children, initHandler, loadingFallback, errorHandler, }) {
11
+ export function SQLiteProvider({ children, onError, useSuspense = false, ...props }) {
12
+ if (onError != null && useSuspense) {
13
+ throw new Error('Cannot use `onError` with `useSuspense`, use error boundaries instead.');
14
+ }
15
+ if (useSuspense) {
16
+ return <SQLiteProviderSuspense {...props}>{children}</SQLiteProviderSuspense>;
17
+ }
18
+ return (<SQLiteProviderNonSuspense {...props} onError={onError}>
19
+ {children}
20
+ </SQLiteProviderNonSuspense>);
21
+ }
22
+ /**
23
+ * A global hook for accessing the SQLite database across components.
24
+ * This hook should only be used within a [`<SQLiteProvider>`](#sqliteprovider) component.
25
+ *
26
+ * @example
27
+ * ```tsx
28
+ * export default function App() {
29
+ * return (
30
+ * <SQLiteProvider databaseName="test.db">
31
+ * <Main />
32
+ * </SQLiteProvider>
33
+ * );
34
+ * }
35
+ *
36
+ * export function Main() {
37
+ * const db = useSQLiteContext();
38
+ * console.log('sqlite version', db.getSync('SELECT sqlite_version()'));
39
+ * return <View />
40
+ * }
41
+ * ```
42
+ */
43
+ export function useSQLiteContext() {
44
+ const context = useContext(SQLiteContext);
45
+ if (context == null) {
46
+ throw new Error('useSQLiteContext must be used within a <SQLiteProvider>');
47
+ }
48
+ return context;
49
+ }
50
+ let databaseInstance = null;
51
+ function SQLiteProviderSuspense({ databaseName, options, children, onInit, }) {
52
+ const databasePromise = getDatabaseAsync({
53
+ databaseName,
54
+ options,
55
+ onInit,
56
+ });
57
+ const database = use(databasePromise);
58
+ return <SQLiteContext.Provider value={database}>{children}</SQLiteContext.Provider>;
59
+ }
60
+ function SQLiteProviderNonSuspense({ databaseName, options, children, onInit, onError, }) {
12
61
  const databaseRef = useRef(null);
13
62
  const [loading, setLoading] = useState(true);
14
63
  const [error, setError] = useState(null);
15
64
  useEffect(() => {
16
65
  async function setup() {
17
66
  try {
18
- const db = await openDatabaseAsync(dbName, options);
19
- if (initHandler != null) {
20
- await initHandler(db);
21
- }
67
+ const db = await openDatabaseWithInitAsync({ databaseName, options, onInit });
22
68
  databaseRef.current = db;
23
69
  setLoading(false);
24
70
  }
@@ -41,45 +87,87 @@ export function SQLiteProvider({ dbName, options, children, initHandler, loading
41
87
  databaseRef.current = null;
42
88
  setLoading(true);
43
89
  };
44
- }, [dbName, options, initHandler]);
90
+ }, [databaseName, options, onInit]);
45
91
  if (error != null) {
46
- const handler = errorHandler ??
92
+ const handler = onError ??
47
93
  ((e) => {
48
94
  throw e;
49
95
  });
50
96
  handler(error);
51
97
  }
52
98
  if (loading || !databaseRef.current) {
53
- return loadingFallback != null ? <>{loadingFallback}</> : null;
99
+ return null;
54
100
  }
55
101
  return <SQLiteContext.Provider value={databaseRef.current}>{children}</SQLiteContext.Provider>;
56
102
  }
103
+ function getDatabaseAsync({ databaseName, options, onInit, }) {
104
+ if (databaseInstance?.promise != null &&
105
+ databaseInstance?.databaseName === databaseName &&
106
+ databaseInstance?.options === options &&
107
+ databaseInstance?.onInit === onInit) {
108
+ return databaseInstance.promise;
109
+ }
110
+ let promise;
111
+ if (databaseInstance?.promise != null) {
112
+ promise = databaseInstance.promise
113
+ .then((db) => {
114
+ db.closeAsync();
115
+ })
116
+ .then(() => {
117
+ return openDatabaseWithInitAsync({ databaseName, options, onInit });
118
+ });
119
+ }
120
+ else {
121
+ promise = openDatabaseWithInitAsync({ databaseName, options, onInit });
122
+ }
123
+ databaseInstance = {
124
+ databaseName,
125
+ options,
126
+ onInit,
127
+ promise,
128
+ };
129
+ return promise;
130
+ }
131
+ async function openDatabaseWithInitAsync({ databaseName, options, onInit, }) {
132
+ const database = await openDatabaseAsync(databaseName, options);
133
+ if (onInit != null) {
134
+ await onInit(database);
135
+ }
136
+ return database;
137
+ }
138
+ // Referenced from https://github.com/reactjs/react.dev/blob/6570e6cd79a16ac3b1a2902632eddab7e6abb9ad/src/content/reference/react/Suspense.md
57
139
  /**
58
- * A global hook for accessing the SQLite database across components.
59
- * This hook should only be used within a [`<SQLiteProvider>`](#sqliteprovider) component.
60
- *
61
- * @example
62
- * ```tsx
63
- * export default function App() {
64
- * return (
65
- * <SQLiteProvider dbName="test.db">
66
- * <Main />
67
- * </SQLiteProvider>
68
- * );
69
- * }
70
- *
71
- * export function Main() {
72
- * const db = useSQLiteContext();
73
- * console.log('sqlite version', db.getSync('SELECT sqlite_version()'));
74
- * return <View />
75
- * }
76
- * ```
140
+ * A custom hook like [`React.use`](https://react.dev/reference/react/use) hook using private Suspense implementation.
77
141
  */
78
- export function useSQLiteContext() {
79
- const context = useContext(SQLiteContext);
80
- if (context == null) {
81
- throw new Error('useSQLiteContext must be used within a <SQLiteProvider>');
142
+ function use(promise) {
143
+ if (isReactUsePromise(promise)) {
144
+ if (promise.status === 'fulfilled') {
145
+ if (promise.value === undefined) {
146
+ throw new Error('[use] Unexpected undefined value from promise');
147
+ }
148
+ return promise.value;
149
+ }
150
+ else if (promise.status === 'rejected') {
151
+ throw promise.reason;
152
+ }
153
+ else if (promise.status === 'pending') {
154
+ throw promise;
155
+ }
156
+ throw new Error('[use] Promise is in an invalid state');
82
157
  }
83
- return context;
158
+ const suspensePromise = promise;
159
+ suspensePromise.status = 'pending';
160
+ suspensePromise.then((result) => {
161
+ suspensePromise.status = 'fulfilled';
162
+ suspensePromise.value = result;
163
+ }, (reason) => {
164
+ suspensePromise.status = 'rejected';
165
+ suspensePromise.reason = reason;
166
+ });
167
+ throw suspensePromise;
168
+ }
169
+ function isReactUsePromise(promise) {
170
+ return typeof promise === 'object' && promise !== null && 'status' in promise;
84
171
  }
172
+ //#endregion
85
173
  //# sourceMappingURL=hooks.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/next/hooks.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEtF,OAAO,EAAE,iBAAiB,EAAiB,MAAM,YAAY,CAAC;AAsC9D;;GAEG;AACH,MAAM,aAAa,GAAG,aAAa,CAAkB,IAAI,CAAC,CAAC;AAE3D;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,EAC7B,MAAM,EACN,OAAO,EACP,QAAQ,EACR,WAAW,EACX,eAAe,EACf,YAAY,GACQ;IACpB,MAAM,WAAW,GAAG,MAAM,CAAkB,IAAI,CAAC,CAAC;IAClD,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAe,IAAI,CAAC,CAAC;IAEvD,SAAS,CAAC,GAAG,EAAE;QACb,KAAK,UAAU,KAAK;YAClB,IAAI;gBACF,MAAM,EAAE,GAAG,MAAM,iBAAiB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBACpD,IAAI,WAAW,IAAI,IAAI,EAAE;oBACvB,MAAM,WAAW,CAAC,EAAE,CAAC,CAAC;iBACvB;gBACD,WAAW,CAAC,OAAO,GAAG,EAAE,CAAC;gBACzB,UAAU,CAAC,KAAK,CAAC,CAAC;aACnB;YAAC,OAAO,CAAC,EAAE;gBACV,QAAQ,CAAC,CAAC,CAAC,CAAC;aACb;QACH,CAAC;QAED,KAAK,UAAU,QAAQ,CAAC,EAAmB;YACzC,IAAI;gBACF,MAAM,EAAE,EAAE,UAAU,EAAE,CAAC;aACxB;YAAC,OAAO,CAAC,EAAE;gBACV,QAAQ,CAAC,CAAC,CAAC,CAAC;aACb;QACH,CAAC;QAED,KAAK,EAAE,CAAC;QAER,OAAO,GAAG,EAAE;YACV,MAAM,EAAE,GAAG,WAAW,CAAC,OAAO,CAAC;YAC/B,QAAQ,CAAC,EAAE,CAAC,CAAC;YACb,WAAW,CAAC,OAAO,GAAG,IAAI,CAAC;YAC3B,UAAU,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;IAEnC,IAAI,KAAK,IAAI,IAAI,EAAE;QACjB,MAAM,OAAO,GACX,YAAY;YACZ,CAAC,CAAC,CAAC,EAAE,EAAE;gBACL,MAAM,CAAC,CAAC;YACV,CAAC,CAAC,CAAC;QACL,OAAO,CAAC,KAAK,CAAC,CAAC;KAChB;IAED,IAAI,OAAO,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE;QACnC,OAAO,eAAe,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;KAChE;IACD,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;AACjG,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,gBAAgB;IAC9B,MAAM,OAAO,GAAG,UAAU,CAAC,aAAa,CAAC,CAAC;IAC1C,IAAI,OAAO,IAAI,IAAI,EAAE;QACnB,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;KAC5E;IACD,OAAO,OAAO,CAAC;AACjB,CAAC","sourcesContent":["import React, { createContext, useContext, useEffect, useRef, useState } from 'react';\n\nimport { openDatabaseAsync, type Database } from './Database';\nimport type { OpenOptions } from './NativeDatabase';\n\nexport interface SQLiteProviderProps {\n /**\n * The name of the database file to open.\n */\n dbName: string;\n\n /**\n * Open options.\n */\n options?: OpenOptions;\n\n /**\n * The children to render.\n */\n children: React.ReactNode;\n\n /**\n * A custom initialization handler to run before rendering the children.\n * You can use this to run database migrations or other setup tasks.\n */\n initHandler?: (db: Database) => Promise<void>;\n\n /**\n * A custom loading fallback to render before the database is ready.\n * @default null\n */\n loadingFallback?: React.ReactNode;\n\n /**\n * Handle errors from SQLiteProvider.\n * @default rethrow the error\n */\n errorHandler?: (error: Error) => void;\n}\n\n/**\n * Create a context for the SQLite database\n */\nconst SQLiteContext = createContext<Database | null>(null);\n\n/**\n * Context.Provider component that provides a SQLite database to all children.\n * All descendants of this component will be able to access the database using the [`useSQLiteContext`](#usesqlitecontext) hook.\n */\nexport function SQLiteProvider({\n dbName,\n options,\n children,\n initHandler,\n loadingFallback,\n errorHandler,\n}: SQLiteProviderProps) {\n const databaseRef = useRef<Database | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n async function setup() {\n try {\n const db = await openDatabaseAsync(dbName, options);\n if (initHandler != null) {\n await initHandler(db);\n }\n databaseRef.current = db;\n setLoading(false);\n } catch (e) {\n setError(e);\n }\n }\n\n async function teardown(db: Database | null) {\n try {\n await db?.closeAsync();\n } catch (e) {\n setError(e);\n }\n }\n\n setup();\n\n return () => {\n const db = databaseRef.current;\n teardown(db);\n databaseRef.current = null;\n setLoading(true);\n };\n }, [dbName, options, initHandler]);\n\n if (error != null) {\n const handler =\n errorHandler ??\n ((e) => {\n throw e;\n });\n handler(error);\n }\n\n if (loading || !databaseRef.current) {\n return loadingFallback != null ? <>{loadingFallback}</> : null;\n }\n return <SQLiteContext.Provider value={databaseRef.current}>{children}</SQLiteContext.Provider>;\n}\n\n/**\n * A global hook for accessing the SQLite database across components.\n * This hook should only be used within a [`<SQLiteProvider>`](#sqliteprovider) component.\n *\n * @example\n * ```tsx\n * export default function App() {\n * return (\n * <SQLiteProvider dbName=\"test.db\">\n * <Main />\n * </SQLiteProvider>\n * );\n * }\n *\n * export function Main() {\n * const db = useSQLiteContext();\n * console.log('sqlite version', db.getSync('SELECT sqlite_version()'));\n * return <View />\n * }\n * ```\n */\nexport function useSQLiteContext(): Database {\n const context = useContext(SQLiteContext);\n if (context == null) {\n throw new Error('useSQLiteContext must be used within a <SQLiteProvider>');\n }\n return context;\n}\n"]}
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/next/hooks.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { createContext, useContext, useEffect, useRef, useState } from 'react';\n\nimport type { SQLiteOpenOptions } from './NativeDatabase';\nimport { openDatabaseAsync, type SQLiteDatabase } from './SQLiteDatabase';\n\nexport interface SQLiteProviderProps {\n /**\n * The name of the database file to open.\n */\n databaseName: string;\n\n /**\n * Open options.\n */\n options?: SQLiteOpenOptions;\n\n /**\n * The children to render.\n */\n children: React.ReactNode;\n\n /**\n * A custom initialization handler to run before rendering the children.\n * You can use this to run database migrations or other setup tasks.\n */\n onInit?: (db: SQLiteDatabase) => Promise<void>;\n\n /**\n * Handle errors from SQLiteProvider.\n * @default rethrow the error\n */\n onError?: (error: Error) => void;\n\n /**\n * Enable [`React.Suspense`](https://react.dev/reference/react/Suspense) integration.\n * @default false\n * @example\n * ```tsx\n * export default function App() {\n * return (\n * <Suspense fallback={<Text>Loading...</Text>}>\n * <SQLiteProvider databaseName=\"test.db\" useSuspense={true}>\n * <Main />\n * </SQLiteProvider>\n * </Suspense>\n * );\n * }\n * ```\n */\n useSuspense?: boolean;\n}\n\n/**\n * Create a context for the SQLite database\n */\nconst SQLiteContext = createContext<SQLiteDatabase | null>(null);\n\n/**\n * Context.Provider component that provides a SQLite database to all children.\n * All descendants of this component will be able to access the database using the [`useSQLiteContext`](#usesqlitecontext) hook.\n */\nexport function SQLiteProvider({\n children,\n onError,\n useSuspense = false,\n ...props\n}: SQLiteProviderProps) {\n if (onError != null && useSuspense) {\n throw new Error('Cannot use `onError` with `useSuspense`, use error boundaries instead.');\n }\n\n if (useSuspense) {\n return <SQLiteProviderSuspense {...props}>{children}</SQLiteProviderSuspense>;\n }\n\n return (\n <SQLiteProviderNonSuspense {...props} onError={onError}>\n {children}\n </SQLiteProviderNonSuspense>\n );\n}\n\n/**\n * A global hook for accessing the SQLite database across components.\n * This hook should only be used within a [`<SQLiteProvider>`](#sqliteprovider) component.\n *\n * @example\n * ```tsx\n * export default function App() {\n * return (\n * <SQLiteProvider databaseName=\"test.db\">\n * <Main />\n * </SQLiteProvider>\n * );\n * }\n *\n * export function Main() {\n * const db = useSQLiteContext();\n * console.log('sqlite version', db.getSync('SELECT sqlite_version()'));\n * return <View />\n * }\n * ```\n */\nexport function useSQLiteContext(): SQLiteDatabase {\n const context = useContext(SQLiteContext);\n if (context == null) {\n throw new Error('useSQLiteContext must be used within a <SQLiteProvider>');\n }\n return context;\n}\n\n//#region Internals\n\ntype DatabaseInstanceType = Pick<SQLiteProviderProps, 'databaseName' | 'options' | 'onInit'> & {\n promise: Promise<SQLiteDatabase> | null;\n};\n\nlet databaseInstance: DatabaseInstanceType | null = null;\n\nfunction SQLiteProviderSuspense({\n databaseName,\n options,\n children,\n onInit,\n}: Omit<SQLiteProviderProps, 'onError' | 'useSuspense'>) {\n const databasePromise = getDatabaseAsync({\n databaseName,\n options,\n onInit,\n });\n const database = use(databasePromise);\n return <SQLiteContext.Provider value={database}>{children}</SQLiteContext.Provider>;\n}\n\nfunction SQLiteProviderNonSuspense({\n databaseName,\n options,\n children,\n onInit,\n onError,\n}: Omit<SQLiteProviderProps, 'useSuspense'>) {\n const databaseRef = useRef<SQLiteDatabase | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n async function setup() {\n try {\n const db = await openDatabaseWithInitAsync({ databaseName, options, onInit });\n databaseRef.current = db;\n setLoading(false);\n } catch (e) {\n setError(e);\n }\n }\n\n async function teardown(db: SQLiteDatabase | null) {\n try {\n await db?.closeAsync();\n } catch (e) {\n setError(e);\n }\n }\n\n setup();\n\n return () => {\n const db = databaseRef.current;\n teardown(db);\n databaseRef.current = null;\n setLoading(true);\n };\n }, [databaseName, options, onInit]);\n\n if (error != null) {\n const handler =\n onError ??\n ((e) => {\n throw e;\n });\n handler(error);\n }\n if (loading || !databaseRef.current) {\n return null;\n }\n return <SQLiteContext.Provider value={databaseRef.current}>{children}</SQLiteContext.Provider>;\n}\n\nfunction getDatabaseAsync({\n databaseName,\n options,\n onInit,\n}: Pick<SQLiteProviderProps, 'databaseName' | 'options' | 'onInit'>): Promise<SQLiteDatabase> {\n if (\n databaseInstance?.promise != null &&\n databaseInstance?.databaseName === databaseName &&\n databaseInstance?.options === options &&\n databaseInstance?.onInit === onInit\n ) {\n return databaseInstance.promise;\n }\n\n let promise: Promise<SQLiteDatabase>;\n if (databaseInstance?.promise != null) {\n promise = databaseInstance.promise\n .then((db) => {\n db.closeAsync();\n })\n .then(() => {\n return openDatabaseWithInitAsync({ databaseName, options, onInit });\n });\n } else {\n promise = openDatabaseWithInitAsync({ databaseName, options, onInit });\n }\n databaseInstance = {\n databaseName,\n options,\n onInit,\n promise,\n };\n return promise;\n}\n\nasync function openDatabaseWithInitAsync({\n databaseName,\n options,\n onInit,\n}: Pick<SQLiteProviderProps, 'databaseName' | 'options' | 'onInit'>): Promise<SQLiteDatabase> {\n const database = await openDatabaseAsync(databaseName, options);\n if (onInit != null) {\n await onInit(database);\n }\n return database;\n}\n\n//#endregion\n\n//#region Private Suspense API similar to `React.use`\n\n// Referenced from https://github.com/vercel/swr/blob/1d8110900d1aee3747199bfb377b149b7ff6848e/_internal/src/types.ts#L27-L31\ntype ReactUsePromise<T, E extends Error = Error> = Promise<T> & {\n status?: 'pending' | 'fulfilled' | 'rejected';\n value?: T;\n reason?: E;\n};\n\n// Referenced from https://github.com/reactjs/react.dev/blob/6570e6cd79a16ac3b1a2902632eddab7e6abb9ad/src/content/reference/react/Suspense.md\n/**\n * A custom hook like [`React.use`](https://react.dev/reference/react/use) hook using private Suspense implementation.\n */\nfunction use<T>(promise: Promise<T> | ReactUsePromise<T>) {\n if (isReactUsePromise(promise)) {\n if (promise.status === 'fulfilled') {\n if (promise.value === undefined) {\n throw new Error('[use] Unexpected undefined value from promise');\n }\n return promise.value;\n } else if (promise.status === 'rejected') {\n throw promise.reason;\n } else if (promise.status === 'pending') {\n throw promise;\n }\n throw new Error('[use] Promise is in an invalid state');\n }\n\n const suspensePromise = promise as ReactUsePromise<T>;\n suspensePromise.status = 'pending';\n suspensePromise.then(\n (result: T) => {\n suspensePromise.status = 'fulfilled';\n suspensePromise.value = result;\n },\n (reason) => {\n suspensePromise.status = 'rejected';\n suspensePromise.reason = reason;\n }\n );\n throw suspensePromise;\n}\n\nfunction isReactUsePromise<T>(\n promise: Promise<T> | ReactUsePromise<T>\n): promise is ReactUsePromise<T> {\n return typeof promise === 'object' && promise !== null && 'status' in promise;\n}\n\n//#endregion\n"]}