expo-sqlite 58.0.10 → 58.0.12

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 (88) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/android/build.gradle +6 -2
  3. package/android/src/main/cpp/NativeDatabaseBinding.cpp +11 -8
  4. package/android/src/main/cpp/NativeStatementBinding.cpp +43 -2
  5. package/android/src/main/cpp/NativeStatementBinding.h +3 -0
  6. package/android/src/main/cpp/SQLiteError.h +34 -0
  7. package/android/src/main/java/expo/modules/sqlite/DatabaseConnection.kt +68 -0
  8. package/android/src/main/java/expo/modules/sqlite/NativeDatabase.kt +135 -22
  9. package/android/src/main/java/expo/modules/sqlite/NativeSession.kt +126 -6
  10. package/android/src/main/java/expo/modules/sqlite/NativeStatement.kt +199 -14
  11. package/android/src/main/java/expo/modules/sqlite/NativeStatementBinding.kt +1 -0
  12. package/android/src/main/java/expo/modules/sqlite/SQLRecords.kt +19 -13
  13. package/android/src/main/java/expo/modules/sqlite/SQLiteModule.kt +176 -577
  14. package/android/src/main/java/expo/modules/sqlite/SQLiteOptions.kt +3 -10
  15. package/android/src/main/java/expo/modules/sqlite/SQLiteValues.kt +47 -0
  16. package/build/NativeDatabase.d.ts +2 -2
  17. package/build/SQLiteDatabase.js +9 -3
  18. package/build/SQLiteDatabase.js.map +1 -1
  19. package/build/WebConfiguration.d.ts +31 -0
  20. package/build/WebConfiguration.js +21 -0
  21. package/build/WebConfiguration.js.map +1 -0
  22. package/build/index.d.ts +1 -0
  23. package/build/index.js +1 -0
  24. package/build/index.js.map +1 -1
  25. package/dev-plugin-dist/+not-found.html +2 -4
  26. package/dev-plugin-dist/_expo/static/js/web/{__common-9b94dd2fbdbb825a662f42cf8d2077ee.js → __common-3e95e247f8ee6d975454b07047f39148.js} +52 -48
  27. package/dev-plugin-dist/_expo/static/js/web/{_layout-b2894030c1dfbd95b717a85901b6c271.js → _layout-0bc906c56b363d38baf86696db19957e.js} +23 -23
  28. package/dev-plugin-dist/_expo/static/js/web/{entry-04fdb7d857a65c0ed638ef2d774a4b6e.js → entry-293efbd088ab66af1b3334c3eeafa61f.js} +697 -696
  29. package/dev-plugin-dist/_expo/static/js/web/{index-4bf2da199f699eb47a42074d5e183c07.js → index-454a4f95007576080f4f47d70441d41a.js} +1740 -1740
  30. package/dev-plugin-dist/_expo/static/js/web/{worker-d51aa6be89e271e571111274ab8e7f5c.js → worker-172ab4a50823f055f5bd777d90d1a36a.js} +17 -17
  31. package/dev-plugin-dist/_sitemap.html +2 -4
  32. package/dev-plugin-dist/index.html +2 -4
  33. package/expo-module.config.json +0 -4
  34. package/ios/DatabaseFileUtils.swift +101 -5
  35. package/ios/NativeDatabase.swift +5 -8
  36. package/ios/NativeStatement.swift +20 -13
  37. package/ios/SQLiteModule.swift +8 -2
  38. package/ios/SQLiteResult.swift +31 -0
  39. package/ios/Tests/DatabaseFileUtilsTests.swift +119 -0
  40. package/ios/Tests/test_ErrorReporting.swift +25 -0
  41. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12-sources.jar +0 -0
  42. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12-sources.jar.md5 +1 -0
  43. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12-sources.jar.sha1 +1 -0
  44. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12-sources.jar.sha256 +1 -0
  45. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12-sources.jar.sha512 +1 -0
  46. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.aar +0 -0
  47. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.aar.md5 +1 -0
  48. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.aar.sha1 +1 -0
  49. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.aar.sha256 +1 -0
  50. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.aar.sha512 +1 -0
  51. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/{58.0.10/expo.modules.sqlite-58.0.10.module → 58.0.12/expo.modules.sqlite-58.0.12.module} +22 -22
  52. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.module.md5 +1 -0
  53. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.module.sha1 +1 -0
  54. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.module.sha256 +1 -0
  55. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.module.sha512 +1 -0
  56. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/{58.0.10/expo.modules.sqlite-58.0.10.pom → 58.0.12/expo.modules.sqlite-58.0.12.pom} +1 -1
  57. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.pom.md5 +1 -0
  58. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.pom.sha1 +1 -0
  59. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.pom.sha256 +1 -0
  60. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.pom.sha512 +1 -0
  61. package/package.json +4 -4
  62. package/src/NativeDatabase.ts +2 -2
  63. package/src/SQLiteDatabase.ts +11 -3
  64. package/src/WebConfiguration.ts +36 -0
  65. package/src/index.ts +1 -0
  66. package/web/SQLiteModule.ts +16 -0
  67. package/web/WorkerChannel.ts +17 -9
  68. package/web/wa-sqlite/sqlite-api.js +4 -1
  69. package/web/web.types.ts +6 -0
  70. package/web/worker.ts +50 -38
  71. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10-sources.jar +0 -0
  72. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10-sources.jar.md5 +0 -1
  73. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10-sources.jar.sha1 +0 -1
  74. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10-sources.jar.sha256 +0 -1
  75. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10-sources.jar.sha512 +0 -1
  76. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.aar +0 -0
  77. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.aar.md5 +0 -1
  78. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.aar.sha1 +0 -1
  79. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.aar.sha256 +0 -1
  80. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.aar.sha512 +0 -1
  81. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.module.md5 +0 -1
  82. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.module.sha1 +0 -1
  83. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.module.sha256 +0 -1
  84. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.module.sha512 +0 -1
  85. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.pom.md5 +0 -1
  86. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.pom.sha1 +0 -1
  87. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.pom.sha256 +0 -1
  88. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.pom.sha512 +0 -1
@@ -0,0 +1 @@
1
+ 48f8da8c6d5d961978b986c2e3c2b2326e76c82e330f2d6a5119d37a8330f4756480e0760a22c67dc74039b0e76abb796bdf07720490bfb2c2df4d912c692214
@@ -9,7 +9,7 @@
9
9
  <modelVersion>4.0.0</modelVersion>
10
10
  <groupId>host.exp.exponent</groupId>
11
11
  <artifactId>expo.modules.sqlite</artifactId>
12
- <version>58.0.10</version>
12
+ <version>58.0.12</version>
13
13
  <packaging>aar</packaging>
14
14
  <name>expo.modules.sqlite</name>
15
15
  <url>https://github.com/expo/expo</url>
@@ -0,0 +1 @@
1
+ efe34bb452ecd1da1e59083dfef52aa7e884e163b6808b2bbc0b27bab0017754
@@ -0,0 +1 @@
1
+ b60567105d26b5f2ed28515989f0a566ad4609df6957e35e3d0f988e011de482e86fb975aa520196abcca1a91cdbf6f422a6db01c966312cd6a98bb5dd7e8b55
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-sqlite",
3
- "version": "58.0.10",
3
+ "version": "58.0.12",
4
4
  "description": "Provides access to a database using SQLite (https://www.sqlite.org/). The database is persisted across restarts of your app.",
5
5
  "keywords": [
6
6
  "async-storage",
@@ -50,13 +50,13 @@
50
50
  "await-lock": "^2.2.2"
51
51
  },
52
52
  "devDependencies": {
53
+ "@expo/internal-scripts": "58.0.1",
53
54
  "@expo/spawn-async": "^1.8.0",
54
55
  "@testing-library/react-native": "^14.0.1",
55
56
  "@types/jest": "^30.0.0",
56
57
  "@types/node": "^22.14.0",
57
58
  "@types/react": "~19.3.0",
58
- "expo": "58.0.4",
59
- "expo-module-scripts": "56.0.7",
59
+ "expo": "58.0.8",
60
60
  "react-error-boundary": "^4.0.11",
61
61
  "tar": "^7.5.22"
62
62
  },
@@ -66,7 +66,7 @@
66
66
  "react-native": "*"
67
67
  },
68
68
  "jest": {
69
- "preset": "expo-module-scripts"
69
+ "preset": "@expo/internal-scripts"
70
70
  },
71
71
  "scripts": {
72
72
  "precompile-android": "et prebuild-android-package-for-publish",
@@ -13,7 +13,7 @@ export declare class NativeDatabase {
13
13
  public isInTransactionAsync(): Promise<boolean>;
14
14
  public closeAsync(): Promise<void>;
15
15
  public execAsync(source: string): Promise<void>;
16
- public serializeAsync(databaseName: string): Promise<Uint8Array>;
16
+ public serializeAsync(databaseName: string): Promise<Uint8Array | ArrayBuffer>;
17
17
  public prepareAsync(nativeStatement: NativeStatement, source: string): Promise<NativeStatement>;
18
18
  public createSessionAsync(nativeSession: NativeSession, dbName: string): Promise<NativeSession>;
19
19
  public loadExtensionAsync(libPath: string, entryPoint?: string): Promise<void>;
@@ -27,7 +27,7 @@ export declare class NativeDatabase {
27
27
  public closeSync(): void;
28
28
  public interruptSync(): void;
29
29
  public execSync(source: string): void;
30
- public serializeSync(databaseName: string): Uint8Array;
30
+ public serializeSync(databaseName: string): Uint8Array | ArrayBuffer;
31
31
  public prepareSync(nativeStatement: NativeStatement, source: string): NativeStatement;
32
32
  public createSessionSync(nativeSession: NativeSession, dbName: string): NativeSession;
33
33
  public loadExtensionSync(libPath: string, entryPoint?: string): void;
@@ -63,8 +63,8 @@ export class SQLiteDatabase {
63
63
  *
64
64
  * @param databaseName The name of the current attached databases. The default value is `main` which is the default database name.
65
65
  */
66
- public serializeAsync(databaseName: string = 'main'): Promise<Uint8Array> {
67
- return this.nativeDatabase.serializeAsync(databaseName);
66
+ public async serializeAsync(databaseName: string = 'main'): Promise<Uint8Array> {
67
+ return toUint8Array(await this.nativeDatabase.serializeAsync(databaseName));
68
68
  }
69
69
 
70
70
  /**
@@ -264,7 +264,7 @@ export class SQLiteDatabase {
264
264
  * @param databaseName The name of the current attached databases. The default value is `main` which is the default database name.
265
265
  */
266
266
  public serializeSync(databaseName: string = 'main'): Uint8Array {
267
- return this.nativeDatabase.serializeSync(databaseName);
267
+ return toUint8Array(this.nativeDatabase.serializeSync(databaseName));
268
268
  }
269
269
 
270
270
  /**
@@ -802,3 +802,11 @@ class Transaction extends SQLiteDatabase {
802
802
  return new Transaction(db.databasePath, options, nativeDatabase);
803
803
  }
804
804
  }
805
+
806
+ /**
807
+ * The native database hands serialized bytes over as a `Uint8Array` on iOS and as an `ArrayBuffer`
808
+ * on Android.
809
+ */
810
+ function toUint8Array(data: Uint8Array | ArrayBuffer): Uint8Array {
811
+ return data instanceof Uint8Array ? data : new Uint8Array(data);
812
+ }
@@ -0,0 +1,36 @@
1
+ import ExpoSQLite from './ExpoSQLite';
2
+
3
+ /**
4
+ * Options for [`configureWeb()`](#sqliteconfigureweboptions).
5
+ * @platform web
6
+ */
7
+ export interface SQLiteWebOptions {
8
+ /**
9
+ * The URL of a custom wa-sqlite WebAssembly build, for example one built with FTS enabled.
10
+ * When omitted, `expo-sqlite` uses its bundled build.
11
+ *
12
+ * The build must come from the [`expo/wa-sqlite`](https://github.com/expo/wa-sqlite) revision that matches your `expo-sqlite` version.
13
+ * A build from another revision may fail to load.
14
+ */
15
+ wasmURL?: string;
16
+ }
17
+
18
+ /**
19
+ * Configures how `expo-sqlite` runs on web.
20
+ * Each call replaces the options from the previous call.
21
+ * It has no effect on Android, iOS, and server rendering.
22
+ *
23
+ * Call it before opening any database.
24
+ * It throws if `wasmURL` changes after a database is opened, because the web worker loads SQLite only once.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * SQLite.configureWeb({ wasmURL: require('./assets/wa-sqlite-fts.wasm') });
29
+ * const db = await SQLite.openDatabaseAsync('app.db');
30
+ * ```
31
+ *
32
+ * @platform web
33
+ */
34
+ export function configureWeb(options: SQLiteWebOptions): void {
35
+ ExpoSQLite.configureWeb?.(options);
36
+ }
package/src/index.ts CHANGED
@@ -3,3 +3,4 @@ export * from './SQLiteSession';
3
3
  export * from './SQLiteStatement';
4
4
  export * from './SQLiteTaggedQuery';
5
5
  export * from './hooks';
6
+ export { configureWeb, type SQLiteWebOptions } from './WebConfiguration';
@@ -11,9 +11,12 @@ import {
11
11
  type SQLiteColumnValues,
12
12
  type SQLiteRunResult,
13
13
  } from '../src/NativeStatement';
14
+ import { type SQLiteWebOptions } from '../src/WebConfiguration';
14
15
  import { invokeWorkerAsync, invokeWorkerSync, workerMessageHandler } from './WorkerChannel';
16
+ import { type ConfigureWorkerMessage } from './web.types';
15
17
 
16
18
  let worker: Worker | null = null;
19
+ let webOptions: SQLiteWebOptions = {};
17
20
  let nextNativeDatabaseId = 0;
18
21
  let nextNativeStatementId = 0;
19
22
  let nextNativeSessionId = 0;
@@ -29,6 +32,7 @@ function getWorker(): Worker {
29
32
  }
30
33
  workerMessageHandler(event);
31
34
  });
35
+ worker.postMessage({ type: 'configure', data: webOptions } satisfies ConfigureWorkerMessage);
32
36
  }
33
37
  return worker;
34
38
  }
@@ -427,6 +431,18 @@ export class SQLiteModule extends NativeModule {
427
431
  });
428
432
  }
429
433
 
434
+ configureWeb(options: SQLiteWebOptions): void {
435
+ if (worker != null) {
436
+ // Fast Refresh runs the same call again after a database is open.
437
+ if (options.wasmURL === webOptions.wasmURL) return;
438
+ throw new Error(
439
+ 'Cannot change the SQLite WebAssembly URL because a database has already been opened, and the web worker loads SQLite only once. ' +
440
+ 'Call `configureWeb()` before opening any database, and reload the app after you change `wasmURL`.'
441
+ );
442
+ }
443
+ webOptions = { ...options };
444
+ }
445
+
430
446
  async ensureDatabasePathExistsAsync(databasePath: string): Promise<void> {
431
447
  // No-op for web
432
448
  }
@@ -33,21 +33,29 @@ export function sendWorkerResult({
33
33
  resultBuffer: SharedArrayBuffer;
34
34
  };
35
35
  }) {
36
+ // Send the message as a string. An Error doesn't survive JSON, and `String(error)` keeps it non-empty.
37
+ const errorMessage = error != null ? error.message || String(error) : null;
36
38
  if (syncTrait) {
37
39
  const { lockBuffer, resultBuffer } = syncTrait;
38
40
  const lock = new Int32Array(lockBuffer);
39
- const resultArray = new Uint8Array(resultBuffer);
40
- const resultJson = error != null ? serialize({ error }) : serialize({ result });
41
- const resultBytes = new TextEncoder().encode(resultJson);
42
- const length = resultBytes.length;
43
- resultArray.set(new Uint32Array([length]), 0);
44
- resultArray.set(resultBytes, 4);
41
+ let resultBytes = new TextEncoder().encode(
42
+ errorMessage != null ? serialize({ error: errorMessage }) : serialize({ result })
43
+ );
44
+ if (resultBytes.length + 4 > resultBuffer.byteLength) {
45
+ resultBytes = new TextEncoder().encode(
46
+ serialize({
47
+ error: `The synchronous result is ${resultBytes.length} bytes, which is more than the ${resultBuffer.byteLength - 4} bytes that synchronous calls can return on web. Use the async API for large results.`,
48
+ })
49
+ );
50
+ }
51
+ new Uint32Array(resultBuffer, 0, 1)[0] = resultBytes.length;
52
+ new Uint8Array(resultBuffer).set(resultBytes, 4);
45
53
  Atomics.store(lock, 0, RESOLVED);
46
54
  } else {
47
- if (result) {
48
- self.postMessage({ id, result });
55
+ if (error != null) {
56
+ self.postMessage({ id, error: errorMessage });
49
57
  } else {
50
- self.postMessage({ id, error });
58
+ self.postMessage({ id, result });
51
59
  }
52
60
  }
53
61
  }
@@ -300,7 +300,10 @@ export function Factory(Module) {
300
300
  return async function (db) {
301
301
  verifyDatabase(db);
302
302
  const result = await f(db);
303
- databases.delete(db);
303
+ // expo-sqlite: SQLite keeps the connection open when close fails, so keep it usable for a retry.
304
+ if (result === SQLite.SQLITE_OK) {
305
+ databases.delete(db);
306
+ }
304
307
  return check(fname, result, db);
305
308
  };
306
309
  })();
package/web/web.types.ts CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  type SQLiteColumnNames,
9
9
  type SQLiteColumnValues,
10
10
  } from '../src/NativeStatement';
11
+ import { type SQLiteWebOptions } from '../src/WebConfiguration';
11
12
  import { type SQLAction } from './SQLAction';
12
13
 
13
14
  export interface SyncWorkerMessage {
@@ -250,6 +251,11 @@ type SessionInvertChangesetMessage = BaseWorkerMessage & {
250
251
  };
251
252
  };
252
253
 
254
+ export interface ConfigureWorkerMessage {
255
+ type: 'configure';
256
+ data: SQLiteWebOptions;
257
+ }
258
+
253
259
  //#endregion Request messages
254
260
 
255
261
  //#region Response messages
package/web/worker.ts CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  import { type Changeset } from '../src/NativeSession';
6
6
  import { type SQLiteColumnNames, type SQLiteColumnValues } from '../src/NativeStatement';
7
+ import { type SQLiteWebOptions } from '../src/WebConfiguration';
7
8
  import { createSQLAction } from './SQLAction';
8
9
  import { SQLiteOptions } from './SQLiteOptions';
9
10
  import { sendWorkerResult } from './WorkerChannel';
@@ -21,6 +22,7 @@ import WaSQLiteFactory from './wa-sqlite/wa-sqlite';
21
22
  // @ts-expect-error wasm module is not typed
22
23
  import wasmModule from './wa-sqlite/wa-sqlite.wasm';
23
24
  import {
25
+ type ConfigureWorkerMessage,
24
26
  type SQLiteWorkerMessage,
25
27
  type SQLiteWorkerMessageType,
26
28
  type MessageTypeMap,
@@ -40,6 +42,7 @@ interface DatabaseEntity {
40
42
  }
41
43
  interface StatementEntity {
42
44
  pointer: StatementPointer;
45
+ databasePointer: DatabasePointer;
43
46
  }
44
47
  interface SessionEntity {
45
48
  pointer: SessionPointer;
@@ -54,6 +57,7 @@ const MIN_INT32 = -0x80000000;
54
57
  let _sqlite3: SQLiteAPI | null = null;
55
58
  let _vfs: AccessHandlePoolVFS | null = null;
56
59
  let _vfsMemory: MemoryVFS | null = null;
60
+ let _webOptions: SQLiteWebOptions = {};
57
61
 
58
62
  const databaseIdMap = new Map<number, DatabaseEntity>();
59
63
  const statementIdMap = new Map<number, StatementEntity>();
@@ -61,7 +65,12 @@ const sessionIdMap = new Map<number, SessionEntity>();
61
65
 
62
66
  class SQLiteErrorException extends Error {}
63
67
 
64
- self.onmessage = async (event: MessageEvent<SQLiteWorkerMessage>) => {
68
+ self.onmessage = async (event: MessageEvent<SQLiteWorkerMessage | ConfigureWorkerMessage>) => {
69
+ if (event.data.type === 'configure') {
70
+ _webOptions = event.data.data;
71
+ return;
72
+ }
73
+
65
74
  let result: ResultType | null = null;
66
75
  let error: Error | null = null;
67
76
  try {
@@ -118,7 +127,7 @@ async function handleMessageImpl<T extends SQLiteWorkerMessageType>({
118
127
  }
119
128
 
120
129
  case 'finalize': {
121
- await finalize(data.nativeDatabaseId, data.nativeStatementId);
130
+ await finalize(data.nativeStatementId);
122
131
  break;
123
132
  }
124
133
 
@@ -253,12 +262,20 @@ async function backupDatabase(
253
262
  }
254
263
 
255
264
  async function closeDatabase(nativeDatabaseId: number) {
256
- maybeFinalizeAllStatements(nativeDatabaseId);
257
265
  const { sqlite3 } = await maybeInitAsync();
258
266
  const dbEntity = databaseIdMap.get(nativeDatabaseId);
259
- if (dbEntity) {
260
- databaseIdMap.delete(nativeDatabaseId);
267
+ if (!dbEntity) throw new Error(`Database not found - nativeDatabaseId[${nativeDatabaseId}]`);
268
+
269
+ databaseIdMap.delete(nativeDatabaseId);
270
+ if ([...databaseIdMap.values()].includes(dbEntity)) {
271
+ return;
272
+ }
273
+ try {
274
+ await maybeFinalizeAllStatements(sqlite3, dbEntity);
261
275
  await sqlite3.close(dbEntity.pointer);
276
+ } catch (e) {
277
+ databaseIdMap.set(nativeDatabaseId, dbEntity);
278
+ throw e;
262
279
  }
263
280
  }
264
281
 
@@ -289,10 +306,9 @@ async function exec(nativeDatabaseId: number, source: string) {
289
306
  await sqlite3.exec(dbEntity.pointer, source);
290
307
  }
291
308
 
292
- async function finalize(nativeDatabaseId: number, nativeStatementId: number): Promise<void> {
309
+ // A closed handle to a shared connection may still own statements, so don't require its id here.
310
+ async function finalize(nativeStatementId: number): Promise<void> {
293
311
  const { sqlite3 } = await maybeInitAsync();
294
- const dbEntity = databaseIdMap.get(nativeDatabaseId);
295
- if (!dbEntity) throw new Error(`Database not found - nativeDatabaseId[${nativeDatabaseId}]`);
296
312
  const stmt = statementIdMap.get(nativeStatementId);
297
313
  if (!stmt) throw new Error(`Statement not found - nativeStatementId[${nativeStatementId}]`);
298
314
 
@@ -429,7 +445,10 @@ async function prepare(
429
445
  const { value: statementPointer } = await asyncIterator.next();
430
446
  asyncIterator.return?.();
431
447
  if (!statementPointer) throw new Error('Failed to prepare statement');
432
- statementIdMap.set(nativeStatementId, { pointer: statementPointer });
448
+ statementIdMap.set(nativeStatementId, {
449
+ pointer: statementPointer,
450
+ databasePointer: dbEntity.pointer,
451
+ });
433
452
  }
434
453
 
435
454
  async function run(
@@ -736,40 +755,21 @@ async function initDb(sqlite3: SQLiteAPI, dbEntity: DatabaseEntity) {
736
755
  }
737
756
  }
738
757
 
739
- async function maybeFinalizeAllStatements(nativeDatabaseId: number) {
740
- const { sqlite3 } = await maybeInitAsync();
741
- const dbEntity = databaseIdMap.get(nativeDatabaseId);
742
- if (!dbEntity) throw new Error(`Database not found - nativeDatabaseId[${nativeDatabaseId}]`);
758
+ async function maybeFinalizeAllStatements(sqlite3: SQLiteAPI, dbEntity: DatabaseEntity) {
743
759
  if (!dbEntity.openOptions.finalizeUnusedStatementsBeforeClosing) {
744
760
  return;
745
761
  }
746
762
 
747
- let error: Error | null = null;
748
- const finalizedStatements: StatementPointer[] = [];
749
- let stmt: StatementPointer | null = sqlite3.next_stmt(dbEntity.pointer, null);
750
- while (stmt != null && stmt !== 0) {
751
- const nextStmt: StatementPointer = sqlite3.next_stmt(dbEntity.pointer, stmt);
752
- try {
753
- sqlite3.finalize(stmt);
754
- finalizedStatements.push(stmt);
755
- } catch (e) {
756
- error = e;
757
- }
758
- stmt = nextStmt;
759
- }
760
-
761
- // Delete finalized statements from the map
762
- const statementsToDelete: number[] = [];
763
763
  for (const [nativeStatementId, stmtEntity] of statementIdMap.entries()) {
764
- if (finalizedStatements.includes(stmtEntity.pointer)) {
765
- statementsToDelete.push(nativeStatementId);
764
+ if (stmtEntity.databasePointer !== dbEntity.pointer) {
765
+ continue;
766
766
  }
767
- }
768
- for (const nativeStatementId of statementsToDelete) {
769
767
  statementIdMap.delete(nativeStatementId);
768
+ const result = await sqlite3.finalize(stmtEntity.pointer);
769
+ if (result !== SQLITE_OK) {
770
+ console.warn(`Finalizing a statement during close failed - error code[${result}]`);
771
+ }
770
772
  }
771
-
772
- if (error) throw error;
773
773
  }
774
774
 
775
775
  async function maybeInitAsync(): Promise<{
@@ -778,9 +778,7 @@ async function maybeInitAsync(): Promise<{
778
778
  vfsMemory: MemoryVFS;
779
779
  }> {
780
780
  if (!_sqlite3) {
781
- const module = await WaSQLiteFactory({
782
- locateFile: () => wasmModule,
783
- });
781
+ const module = await loadWaSQLiteModuleAsync();
784
782
  _sqlite3 = SQLite.Factory(module) as SQLiteAPI;
785
783
  if (!_sqlite3) {
786
784
  throw new Error('Failed to initialize wa-sqlite');
@@ -808,4 +806,18 @@ async function maybeInitAsync(): Promise<{
808
806
  return { sqlite3: _sqlite3, vfs: _vfs, vfsMemory: _vfsMemory };
809
807
  }
810
808
 
809
+ async function loadWaSQLiteModuleAsync(): Promise<any> {
810
+ const { wasmURL } = _webOptions;
811
+ try {
812
+ return await WaSQLiteFactory({ locateFile: () => wasmURL ?? wasmModule });
813
+ } catch (e) {
814
+ if (wasmURL == null) throw e;
815
+ throw new Error(
816
+ `Failed to load the SQLite WebAssembly module from "${wasmURL}". ` +
817
+ 'Check that the `wasmURL` passed to `configureWeb()` is reachable and built from the `expo/wa-sqlite` revision that this `expo-sqlite` uses. ' +
818
+ `Cause: ${e instanceof Error ? e.message : String(e)}`
819
+ );
820
+ }
821
+ }
822
+
811
823
  //#endregion Internal helpers
@@ -1 +0,0 @@
1
- 67dbdc88a1d7fc60c31092ad150a98a30d93d8ca82bbf869609890965425524d
@@ -1 +0,0 @@
1
- 47d6b003042ef32b80e19b64fd015ffcfa159bb8f08971459211cd6ab971304da58dee6236c2be5319f606f9062d023530dcaa5c2e530bb17e42ab1465d5b95f
@@ -1 +0,0 @@
1
- c4c90adee96c2308551babad7ac1fca8e3782ab14771b68933f1151a03436333
@@ -1 +0,0 @@
1
- 8da2bcca001991403ad15af7ae5fe40b6f02cf613801c3c109fc64b0a25980c07019c47fca22cbfef180ce826225442d307fc3b4416cc29127fda0c3880c666c
@@ -1 +0,0 @@
1
- dfc2cb2d98cfef3bd6b5be8f805dd3444b34c4755dfc2f6ff0e0b653ba123cd3
@@ -1 +0,0 @@
1
- 73ae3ffc162990e89b804689ba3d929767b2619e57e56d78c277b4c772fd8cd284ff0cda1a1418342b5f21c68fd4538548ab273f38c390a31dbd5a6e8800143f
@@ -1 +0,0 @@
1
- d46287f54d70926645728cdb6a436ad843ea632fbdf9274a29c6227de2e61532
@@ -1 +0,0 @@
1
- 584fceef6dc748071a484a1cee817dbc73fc8ce8838fbcb29a1d429a2b8eb36d608d7acd3a17c10f8e35a78ded5064bc01bb839376dc11b59e5c82937a23f4bf