@capgo/capacitor-fast-sql 8.2.16 → 8.2.18

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 (37) hide show
  1. package/README.md +17 -16
  2. package/android/build.gradle +1 -0
  3. package/android/consumer-rules.pro +14 -0
  4. package/android/src/main/java/app/capgo/capacitor/fastsql/CapgoCapacitorFastSqlPlugin.java +29 -6
  5. package/android/src/main/java/app/capgo/capacitor/fastsql/DatabaseConnection.java +2 -0
  6. package/android/src/main/java/app/capgo/capacitor/fastsql/EncryptedSQLDatabase.java +33 -46
  7. package/android/src/main/java/app/capgo/capacitor/fastsql/SQLDatabase.java +32 -50
  8. package/android/src/main/java/app/capgo/capacitor/fastsql/SQLHTTPServer.java +11 -23
  9. package/dist/esm/definitions.d.ts +17 -2
  10. package/dist/esm/definitions.d.ts.map +1 -1
  11. package/dist/esm/definitions.js +3 -1
  12. package/dist/esm/fast-sql.d.ts +4 -0
  13. package/dist/esm/fast-sql.d.ts.map +1 -1
  14. package/dist/esm/fast-sql.js +35 -3
  15. package/dist/esm/native-sql-connection.d.ts +1 -0
  16. package/dist/esm/native-sql-connection.d.ts.map +1 -1
  17. package/dist/esm/native-sql-connection.js +13 -6
  18. package/dist/esm/transaction-isolation.d.ts +3 -0
  19. package/dist/esm/transaction-isolation.d.ts.map +1 -0
  20. package/dist/esm/transaction-isolation.js +6 -0
  21. package/dist/esm/vector-store.d.ts.map +1 -1
  22. package/dist/esm/vector-store.js +5 -2
  23. package/dist/esm/web-sql-connection.d.ts.map +1 -1
  24. package/dist/esm/web-sql-connection.js +2 -0
  25. package/dist/esm/web.d.ts +2 -0
  26. package/dist/esm/web.d.ts.map +1 -1
  27. package/dist/esm/web.js +32 -4
  28. package/dist/plugin.cjs.js +93 -16
  29. package/dist/plugin.cjs.js.map +1 -1
  30. package/dist/plugin.js +93 -16
  31. package/dist/plugin.js.map +1 -1
  32. package/ios/Sources/CapgoCapacitorFastSqlPlugin/CapgoCapacitorFastSqlPlugin.swift +25 -3
  33. package/ios/Sources/CapgoCapacitorFastSqlPlugin/SQLDatabase.swift +15 -5
  34. package/ios/Sources/CapgoCapacitorFastSqlPlugin/SQLHTTPServer.swift +6 -0
  35. package/ios/Tests/CapgoCapacitorFastSqlPluginTests/CapgoCapacitorFastSqlPluginTests.swift +13 -3
  36. package/ios/Tests/CapgoCapacitorFastSqlPluginTests/SQLResultMetadataTests.swift +15 -0
  37. package/package.json +4 -2
package/dist/plugin.js CHANGED
@@ -3,7 +3,9 @@ var capacitorCapgoCapacitorFastSql = (function (exports, core) {
3
3
 
4
4
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
5
5
  /**
6
- * Transaction isolation levels
6
+ * Transaction isolation levels. Only Serializable is supported by the current
7
+ * SQLite backends. Other values are retained for source compatibility but reject
8
+ * before starting a transaction; they are not silently mapped to another level.
7
9
  */
8
10
  exports.IsolationLevel = void 0;
9
11
  (function (IsolationLevel) {
@@ -17,6 +19,12 @@ var capacitorCapgoCapacitorFastSql = (function (exports, core) {
17
19
  web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.CapgoCapacitorFastSqlWeb()),
18
20
  });
19
21
 
22
+ function assertSupportedIsolation(level) {
23
+ if (level !== undefined && level !== exports.IsolationLevel.Serializable) {
24
+ throw new Error('Only SERIALIZABLE transaction isolation is supported');
25
+ }
26
+ }
27
+
20
28
  /**
21
29
  * SQL Connection class that uses HTTP protocol for efficient communication
22
30
  * with native SQLite databases, bypassing Capacitor's standard bridge.
@@ -29,7 +37,12 @@ var capacitorCapgoCapacitorFastSql = (function (exports, core) {
29
37
  this.database = database;
30
38
  this.port = port;
31
39
  this.token = token;
32
- this.baseUrl = `http://localhost:${port}`;
40
+ this.baseUrl = `http://127.0.0.1:${port}`;
41
+ }
42
+ browserFetch(input, init) {
43
+ const savedFetch = Reflect.get(globalThis, 'CapacitorWebFetch');
44
+ const browserFetch = typeof savedFetch === 'function' ? savedFetch : globalThis.fetch;
45
+ return browserFetch.call(globalThis, input, init);
33
46
  }
34
47
  /**
35
48
  * Get the database name
@@ -45,7 +58,7 @@ var capacitorCapgoCapacitorFastSql = (function (exports, core) {
45
58
  * @returns Query results
46
59
  */
47
60
  async execute(statement, params) {
48
- const response = await fetch(`${this.baseUrl}/execute`, {
61
+ const response = await this.browserFetch(`${this.baseUrl}/execute`, {
49
62
  method: 'POST',
50
63
  headers: {
51
64
  'Content-Type': 'application/json',
@@ -71,7 +84,7 @@ var capacitorCapgoCapacitorFastSql = (function (exports, core) {
71
84
  * @returns Array of results for each operation
72
85
  */
73
86
  async executeBatch(operations) {
74
- const response = await fetch(`${this.baseUrl}/batch`, {
87
+ const response = await this.browserFetch(`${this.baseUrl}/batch`, {
75
88
  method: 'POST',
76
89
  headers: {
77
90
  'Content-Type': 'application/json',
@@ -98,10 +111,11 @@ var capacitorCapgoCapacitorFastSql = (function (exports, core) {
98
111
  * @param isolationLevel - Optional isolation level
99
112
  */
100
113
  async beginTransaction(isolationLevel) {
114
+ assertSupportedIsolation(isolationLevel);
101
115
  if (this.inTransaction) {
102
116
  throw new Error('Transaction already in progress');
103
117
  }
104
- const response = await fetch(`${this.baseUrl}/transaction/begin`, {
118
+ const response = await this.browserFetch(`${this.baseUrl}/transaction/begin`, {
105
119
  method: 'POST',
106
120
  headers: {
107
121
  'Content-Type': 'application/json',
@@ -125,7 +139,7 @@ var capacitorCapgoCapacitorFastSql = (function (exports, core) {
125
139
  if (!this.inTransaction) {
126
140
  throw new Error('No transaction in progress');
127
141
  }
128
- const response = await fetch(`${this.baseUrl}/transaction/commit`, {
142
+ const response = await this.browserFetch(`${this.baseUrl}/transaction/commit`, {
129
143
  method: 'POST',
130
144
  headers: {
131
145
  Authorization: `Bearer ${this.token}`,
@@ -145,7 +159,7 @@ var capacitorCapgoCapacitorFastSql = (function (exports, core) {
145
159
  if (!this.inTransaction) {
146
160
  throw new Error('No transaction in progress');
147
161
  }
148
- const response = await fetch(`${this.baseUrl}/transaction/rollback`, {
162
+ const response = await this.browserFetch(`${this.baseUrl}/transaction/rollback`, {
149
163
  method: 'POST',
150
164
  headers: {
151
165
  Authorization: `Bearer ${this.token}`,
@@ -306,6 +320,7 @@ var capacitorCapgoCapacitorFastSql = (function (exports, core) {
306
320
  * @param isolationLevel - Optional isolation level
307
321
  */
308
322
  async beginTransaction(isolationLevel) {
323
+ assertSupportedIsolation(isolationLevel);
309
324
  if (this.inTransaction) {
310
325
  throw new Error('Transaction already in progress');
311
326
  }
@@ -400,12 +415,40 @@ var capacitorCapgoCapacitorFastSql = (function (exports, core) {
400
415
  * @returns SQLConnection instance for executing queries
401
416
  */
402
417
  static async connect(options) {
403
- // Check if already connected
404
- const existing = this.connections.get(options.database);
418
+ var _a;
419
+ const saved = this.openOptions.get(options.database);
420
+ if (saved && !this.optionsMatch(saved, options)) {
421
+ throw new Error(`Database '${options.database}' is already opening or open with different options`);
422
+ }
423
+ const existing = (_a = this.connections.get(options.database)) !== null && _a !== void 0 ? _a : this.opening.get(options.database);
405
424
  if (existing) {
406
425
  this.sharedConnections.add(options.database);
407
426
  return existing;
408
427
  }
428
+ const snapshot = Object.assign({}, options);
429
+ this.openOptions.set(options.database, snapshot);
430
+ const pending = this.open(snapshot);
431
+ this.opening.set(options.database, pending);
432
+ try {
433
+ return await pending;
434
+ }
435
+ catch (error) {
436
+ this.openOptions.delete(options.database);
437
+ this.sharedConnections.delete(options.database);
438
+ throw error;
439
+ }
440
+ finally {
441
+ this.opening.delete(options.database);
442
+ }
443
+ }
444
+ static optionsMatch(left, right) {
445
+ return (Boolean(left.encrypted) === Boolean(right.encrypted) &&
446
+ left.encryptionKey === right.encryptionKey &&
447
+ Boolean(left.readOnly) === Boolean(right.readOnly) &&
448
+ Boolean(left.walMode) === Boolean(right.walMode) &&
449
+ Boolean(left.performancePresets) === Boolean(right.performancePresets));
450
+ }
451
+ static async open(options) {
409
452
  // Connect via native plugin
410
453
  const info = await CapgoCapacitorFastSql.connect(options);
411
454
  // Create connection instance appropriate for the current platform
@@ -428,6 +471,7 @@ var capacitorCapgoCapacitorFastSql = (function (exports, core) {
428
471
  */
429
472
  static async disconnect(database) {
430
473
  var _a;
474
+ await this.opening.get(database);
431
475
  const connection = this.connections.get(database);
432
476
  if (!connection) {
433
477
  throw new Error(`Database '${database}' is not connected`);
@@ -440,6 +484,7 @@ var capacitorCapgoCapacitorFastSql = (function (exports, core) {
440
484
  await CapgoCapacitorFastSql.disconnect({ database });
441
485
  // Remove connection
442
486
  this.connections.delete(database);
487
+ this.openOptions.delete(database);
443
488
  this.sharedConnections.delete(database);
444
489
  this.connectionRetainers.delete(database);
445
490
  this.pendingDisconnects.delete(database);
@@ -491,7 +536,7 @@ var capacitorCapgoCapacitorFastSql = (function (exports, core) {
491
536
  * Close all open connections
492
537
  */
493
538
  static async disconnectAll() {
494
- const databases = Array.from(this.connections.keys());
539
+ const databases = Array.from(new Set([...this.connections.keys(), ...this.opening.keys()]));
495
540
  await Promise.all(databases.map((db) => {
496
541
  this.connectionRetainers.delete(db);
497
542
  this.pendingDisconnects.delete(db);
@@ -507,6 +552,8 @@ var capacitorCapgoCapacitorFastSql = (function (exports, core) {
507
552
  return Array.from(this.connections.keys());
508
553
  }
509
554
  }
555
+ FastSQL.opening = new Map();
556
+ FastSQL.openOptions = new Map();
510
557
  FastSQL.connections = new Map();
511
558
  FastSQL.sharedConnections = new Set();
512
559
  FastSQL.connectionRetainers = new Map();
@@ -858,8 +905,11 @@ var capacitorCapgoCapacitorFastSql = (function (exports, core) {
858
905
  .map((row) => this.rowToGetResult(row))
859
906
  .filter(params.predicate)
860
907
  .map((row) => row.id);
861
- for (const id of ids) {
862
- await this.connection.run(`DELETE FROM ${VECTOR_TABLE} WHERE store = ? AND id = ?`, [this.store, id]);
908
+ for (let offset = 0; offset < ids.length; offset += 100) {
909
+ await this.connection.executeBatch(ids.slice(offset, offset + 100).map((id) => ({
910
+ statement: `DELETE FROM ${VECTOR_TABLE} WHERE store = ? AND id = ?`,
911
+ params: [this.store, id],
912
+ })));
863
913
  }
864
914
  }
865
915
  async query(params) {
@@ -16886,7 +16936,13 @@ var capacitorCapgoCapacitorFastSql = (function (exports, core) {
16886
16936
  * Must be called before connect() when overriding the default worker.
16887
16937
  */
16888
16938
  async configureWeb(config) {
16889
- this.webConfig = config;
16939
+ if (this.promiserPromise || this.databases.size > 0) {
16940
+ throw new Error('Configure web storage before opening the first database');
16941
+ }
16942
+ if (config.requirePersistence && config.useOpfs === false) {
16943
+ throw new Error('requirePersistence requires OPFS');
16944
+ }
16945
+ this.webConfig = Object.assign({}, config);
16890
16946
  this.promiserPromise = null;
16891
16947
  }
16892
16948
  getPromiser() {
@@ -16897,28 +16953,37 @@ var capacitorCapgoCapacitorFastSql = (function (exports, core) {
16897
16953
  return this.promiserPromise;
16898
16954
  }
16899
16955
  dbPath(database) {
16956
+ if (!database || database === '.' || database === '..' || /[/\\\0]/.test(database)) {
16957
+ throw new Error('Database must be a non-empty file name without path separators');
16958
+ }
16900
16959
  return `/capgo-fast-sql/${database}.sqlite3`;
16901
16960
  }
16902
16961
  async connect(options) {
16962
+ if (options.readOnly) {
16963
+ throw new Error('Read-only opens are not supported by the SQLite Wasm Worker transport');
16964
+ }
16903
16965
  if (this.databases.has(options.database)) {
16904
16966
  const existing = this.databases.get(options.database);
16905
16967
  return {
16906
16968
  port: existing.port,
16907
16969
  token: existing.token,
16908
16970
  database: options.database,
16971
+ persistent: existing.persistent,
16909
16972
  };
16910
16973
  }
16911
- const promiser = await this.getPromiser();
16912
16974
  const path = this.dbPath(options.database);
16975
+ const promiser = await this.getPromiser();
16913
16976
  const useOpfs = this.webConfig.useOpfs !== false;
16914
16977
  let openResult;
16915
16978
  if (useOpfs) {
16916
16979
  try {
16917
16980
  openResult = await promiser('open', {
16918
- filename: `file:${path}?vfs=opfs`,
16981
+ filename: `file:/capgo-fast-sql/${encodeURIComponent(options.database)}.sqlite3?vfs=opfs`,
16919
16982
  });
16920
16983
  }
16921
16984
  catch (opfsError) {
16985
+ if (this.webConfig.requirePersistence)
16986
+ throw opfsError;
16922
16987
  console.warn('[CapgoCapacitorFastSql] OPFS unavailable; web database will not persist across reloads. ' +
16923
16988
  'Serve with Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp.', opfsError);
16924
16989
  openResult = await promiser('open', { filename: path });
@@ -16932,7 +16997,16 @@ var capacitorCapgoCapacitorFastSql = (function (exports, core) {
16932
16997
  if (!dbId) {
16933
16998
  throw new Error('Failed to open SQLite Wasm database: missing dbId');
16934
16999
  }
16935
- await this.applyPerformanceConfig(promiser, dbId, options);
17000
+ try {
17001
+ if (this.webConfig.requirePersistence && !persistent) {
17002
+ throw new Error('SQLite Worker did not confirm persistent storage');
17003
+ }
17004
+ await this.applyPerformanceConfig(promiser, dbId, options);
17005
+ }
17006
+ catch (error) {
17007
+ await promiser({ type: 'close', dbId, args: {} });
17008
+ throw error;
17009
+ }
16936
17010
  const token = this.generateToken();
16937
17011
  const port = this.nextPort++;
16938
17012
  this.databases.set(options.database, { dbId, token, port, persistent });
@@ -16940,6 +17014,7 @@ var capacitorCapgoCapacitorFastSql = (function (exports, core) {
16940
17014
  port,
16941
17015
  token,
16942
17016
  database: options.database,
17017
+ persistent,
16943
17018
  };
16944
17019
  }
16945
17020
  async disconnect(options) {
@@ -16960,6 +17035,7 @@ var capacitorCapgoCapacitorFastSql = (function (exports, core) {
16960
17035
  return {
16961
17036
  port: dbInfo.port,
16962
17037
  token: dbInfo.token,
17038
+ persistent: dbInfo.persistent,
16963
17039
  };
16964
17040
  }
16965
17041
  async execute(options) {
@@ -17007,6 +17083,7 @@ var capacitorCapgoCapacitorFastSql = (function (exports, core) {
17007
17083
  }
17008
17084
  }
17009
17085
  async beginTransaction(options) {
17086
+ assertSupportedIsolation(options.isolationLevel);
17010
17087
  await this.execute({
17011
17088
  database: options.database,
17012
17089
  statement: 'BEGIN TRANSACTION',