@powersync/web 0.0.0-dev-20250916075127 → 0.0.0-dev-20250922105207

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 (40) hide show
  1. package/dist/2dcb8a60ed4b341d3046.wasm +0 -0
  2. package/dist/50eaffc7435d7a77ffb5.wasm +0 -0
  3. package/dist/88ca2d3820062df8ea26.wasm +0 -0
  4. package/dist/efe40e50208db1807722.wasm +0 -0
  5. package/dist/index.umd.js +95 -16
  6. package/dist/index.umd.js.map +1 -1
  7. package/dist/worker/SharedSyncImplementation.umd.js +10 -10
  8. package/dist/worker/SharedSyncImplementation.umd.js.map +1 -1
  9. package/dist/worker/WASQLiteDB.umd.js +152 -41
  10. package/dist/worker/WASQLiteDB.umd.js.map +1 -1
  11. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_mc-wa-sqlite-async_mjs.umd.js +15 -3
  12. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_mc-wa-sqlite-async_mjs.umd.js.map +1 -1
  13. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_mc-wa-sqlite_mjs.umd.js +15 -3
  14. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_mc-wa-sqlite_mjs.umd.js.map +1 -1
  15. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_wa-sqlite-async_mjs.umd.js +15 -3
  16. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_wa-sqlite-async_mjs.umd.js.map +1 -1
  17. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_wa-sqlite_mjs.umd.js +15 -3
  18. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_wa-sqlite_mjs.umd.js.map +1 -1
  19. package/dist/worker/node_modules_journeyapps_wa-sqlite_src_examples_AccessHandlePoolVFS_js.umd.js +46 -218
  20. package/dist/worker/node_modules_journeyapps_wa-sqlite_src_examples_AccessHandlePoolVFS_js.umd.js.map +1 -1
  21. package/dist/worker/node_modules_journeyapps_wa-sqlite_src_examples_IDBBatchAtomicVFS_js.umd.js +46 -218
  22. package/dist/worker/node_modules_journeyapps_wa-sqlite_src_examples_IDBBatchAtomicVFS_js.umd.js.map +1 -1
  23. package/dist/worker/node_modules_journeyapps_wa-sqlite_src_examples_OPFSCoopSyncVFS_js.umd.js +46 -218
  24. package/dist/worker/node_modules_journeyapps_wa-sqlite_src_examples_OPFSCoopSyncVFS_js.umd.js.map +1 -1
  25. package/lib/package.json +4 -4
  26. package/lib/src/db/PowerSyncDatabase.js +1 -0
  27. package/lib/src/db/adapters/AsyncDatabaseConnection.d.ts +13 -1
  28. package/lib/src/db/adapters/wa-sqlite/WASQLiteConnection.d.ts +3 -1
  29. package/lib/src/db/adapters/wa-sqlite/WASQLiteConnection.js +28 -2
  30. package/lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.d.ts +1 -1
  31. package/lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.js +6 -2
  32. package/lib/src/worker/db/LogHandler.d.ts +12 -0
  33. package/lib/src/worker/db/LogHandler.js +6 -0
  34. package/lib/src/worker/db/WASQLiteDB.worker.js +80 -17
  35. package/lib/tsconfig.tsbuildinfo +1 -1
  36. package/package.json +5 -5
  37. package/dist/31ba59416bad61e8fb1f.wasm +0 -0
  38. package/dist/d0a1e43030b814ed322f.wasm +0 -0
  39. package/dist/f4ad8bfeb6e6e5326142.wasm +0 -0
  40. package/dist/fbde47713220d7baec73.wasm +0 -0
@@ -59,6 +59,7 @@ export const DEFAULT_MODULE_FACTORIES = {
59
59
  },
60
60
  [WASQLiteVFS.AccessHandlePoolVFS]: async (options) => {
61
61
  let module;
62
+ options.logger?.debug(`Opening VFS with options`, JSON.stringify(options));
62
63
  if (options.encryptionKey) {
63
64
  module = await MultiCipherSyncWASQLiteModuleFactory();
64
65
  }
@@ -67,9 +68,26 @@ export const DEFAULT_MODULE_FACTORIES = {
67
68
  }
68
69
  // @ts-expect-error The types for this static method are missing upstream
69
70
  const { AccessHandlePoolVFS } = await import('@journeyapps/wa-sqlite/src/examples/AccessHandlePoolVFS.js');
71
+ const vfs = new AccessHandlePoolVFS(options.dbFileName, module);
72
+ // Allow extra logs
73
+ const proxiedVFS = new Proxy(vfs, {
74
+ get(target, prop, receiver) {
75
+ const value = Reflect.get(target, prop, receiver);
76
+ // Only wrap methods, not properties
77
+ if (typeof value === 'function') {
78
+ return function (...args) {
79
+ options.logger?.debug(`VFS: Called ${String(prop)} with:`, args);
80
+ return value.apply(vfs, args);
81
+ };
82
+ }
83
+ return value;
84
+ }
85
+ });
86
+ await vfs.isReady();
87
+ // const vfs = await AccessHandlePoolVFS.create(options.dbFileName, module);
70
88
  return {
71
89
  module,
72
- vfs: await AccessHandlePoolVFS.create(options.dbFileName, module)
90
+ vfs: proxiedVFS
73
91
  };
74
92
  },
75
93
  [WASQLiteVFS.OPFSCoopSyncVFS]: async (options) => {
@@ -142,7 +160,9 @@ export class WASqliteConnection extends BaseObserver {
142
160
  async openSQLiteAPI() {
143
161
  const { module, vfs } = await this._moduleFactory({
144
162
  dbFileName: this.options.dbFilename,
145
- encryptionKey: this.options.encryptionKey
163
+ encryptionKey: this.options.encryptionKey,
164
+ debugMode: this.options.debugMode,
165
+ logger: this.options.logger
146
166
  });
147
167
  const sqlite3 = SQLite.Factory(module);
148
168
  sqlite3.vfs_register(vfs, true);
@@ -180,6 +200,7 @@ export class WASqliteConnection extends BaseObserver {
180
200
  }
181
201
  }
182
202
  async init() {
203
+ this.options.logger?.debug('Opening WASQLite connection for', this.options.dbFilename);
183
204
  this._sqliteAPI = await this.openSQLiteAPI();
184
205
  await this.openDB();
185
206
  this.registerBroadcastListeners();
@@ -277,6 +298,7 @@ export class WASqliteConnection extends BaseObserver {
277
298
  });
278
299
  }
279
300
  async close() {
301
+ this.options.logger?.debug('Closing WASQLite connection', this.options.dbFilename);
280
302
  this.broadcastChannel?.close();
281
303
  await this.sqliteAPI.close(this.dbP);
282
304
  }
@@ -325,6 +347,10 @@ export class WASqliteConnection extends BaseObserver {
325
347
  return results.flatMap((resultset) => resultset.rows.map((row) => resultset.columns.map((_, index) => row[index])));
326
348
  }
327
349
  async _execute(sql, bindings) {
350
+ // TODO, this might be redundant
351
+ if (this.options.debugMode) {
352
+ this.options.logger?.debug(`Executing SQL: ${sql}`);
353
+ }
328
354
  const results = [];
329
355
  for await (const stmt of this.sqliteAPI.statements(this.dbP, sql)) {
330
356
  let columns;
@@ -9,7 +9,7 @@ export interface WASQLiteOpenFactoryOptions extends WebSQLOpenFactoryOptions {
9
9
  export interface ResolvedWASQLiteOpenFactoryOptions extends ResolvedWebSQLOpenOptions {
10
10
  vfs: WASQLiteVFS;
11
11
  }
12
- export interface WorkerDBOpenerOptions extends ResolvedWASQLiteOpenFactoryOptions {
12
+ export interface WorkerDBOpenerOptions extends Omit<ResolvedWASQLiteOpenFactoryOptions, 'logger'> {
13
13
  logLevel: ILogLevel;
14
14
  }
15
15
  /**
@@ -52,8 +52,12 @@ export class WASQLiteOpenFactory extends AbstractWebSQLOpenFactory {
52
52
  cacheSizeKb,
53
53
  flags: this.resolvedFlags,
54
54
  encryptionKey: encryptionKey,
55
- logLevel: this.logger.getLevel()
56
- }),
55
+ logLevel: this.logger.getLevel(),
56
+ debugMode: this.options.debugMode
57
+ }, Comlink.proxy((event) => {
58
+ // TODO
59
+ this.logger[event.logLevel.toLocaleLowerCase()]('[DB Worker] ', ...event.messages);
60
+ })),
57
61
  identifier: this.options.dbFilename,
58
62
  onClose: () => {
59
63
  if (workerPort instanceof Worker) {
@@ -0,0 +1,12 @@
1
+ import { BaseListener, BaseObserver } from '@powersync/common';
2
+ export type LogEvent = {
3
+ loggerName: string;
4
+ logLevel: string;
5
+ messages: string[];
6
+ };
7
+ export interface LogHandlerListener extends BaseListener {
8
+ onLog: (event: LogEvent) => void;
9
+ }
10
+ export declare class LogHandler extends BaseObserver<LogHandlerListener> {
11
+ pushLog(entry: LogEvent): void;
12
+ }
@@ -0,0 +1,6 @@
1
+ import { BaseObserver } from '@powersync/common';
2
+ export class LogHandler extends BaseObserver {
3
+ pushLog(entry) {
4
+ this.iterateListeners((l) => l.onLog?.(entry));
5
+ }
6
+ }
@@ -2,21 +2,70 @@
2
2
  * Supports both shared and dedicated workers, based on how the worker is constructed (new SharedWorker vs new Worker()).
3
3
  */
4
4
  import '@journeyapps/wa-sqlite';
5
- import { createBaseLogger, createLogger } from '@powersync/common';
5
+ import { createBaseLogger, createLogger, LogLevel } from '@powersync/common';
6
6
  import * as Comlink from 'comlink';
7
7
  import { WASqliteConnection } from '../../db/adapters/wa-sqlite/WASQLiteConnection';
8
8
  import { getNavigatorLocks } from '../../shared/navigator';
9
- const baseLogger = createBaseLogger();
10
- baseLogger.useDefaults();
11
- const logger = createLogger('db-worker');
9
+ import { LogHandler } from './LogHandler';
12
10
  const DBMap = new Map();
13
11
  const OPEN_DB_LOCK = 'open-wasqlite-db';
12
+ function logToAll(logLevel, ...messages) {
13
+ for (const dbFilename of DBMap.keys()) {
14
+ logBroadcaster.pushLog({
15
+ loggerName: dbFilename,
16
+ logLevel,
17
+ messages
18
+ });
19
+ }
20
+ }
14
21
  let nextClientId = 1;
22
+ function logUnhandledException(error) {
23
+ const errorMessage = `
24
+ Name: ${error.name}
25
+ Cause: ${error.cause}
26
+ Message: ${error.message}
27
+ Stack: ${error.stack}`.trim();
28
+ logToAll(LogLevel.ERROR.name, 'Uncaught Exception in DB worker', errorMessage);
29
+ }
30
+ // Report unhandled exceptions to all loggers
31
+ addEventListener('unhandledrejection', (event) => {
32
+ logUnhandledException(event.reason);
33
+ });
34
+ addEventListener('error', (event) => {
35
+ logUnhandledException(event.error);
36
+ });
37
+ addEventListener('unload', () => {
38
+ logToAll(LogLevel.INFO.name, 'DB worker is unloading');
39
+ Array.from(DBMap.values()).forEach(async (dbConnection) => {
40
+ const { db, clientIds } = dbConnection;
41
+ logToAll(LogLevel.INFO.name, `closing db with ids ${clientIds}`);
42
+ db.close();
43
+ });
44
+ });
45
+ const baseLogger = createBaseLogger();
46
+ const logBroadcaster = new LogHandler();
47
+ const defaultHandler = baseLogger.createDefaultHandler();
48
+ const logHandler = (messages, context) => {
49
+ logBroadcaster.pushLog({
50
+ loggerName: context.name ?? 'unknown',
51
+ logLevel: context.level.name,
52
+ messages: messages.map((m) => String(m))
53
+ });
54
+ defaultHandler(messages, context);
55
+ };
56
+ baseLogger.useDefaults({
57
+ formatter: logHandler
58
+ });
59
+ const workerLogger = createLogger('db-worker');
15
60
  const openWorkerConnection = async (options) => {
16
61
  const connection = new WASqliteConnection(options);
17
62
  return {
18
63
  init: Comlink.proxy(() => connection.init()),
19
- getConfig: Comlink.proxy(() => connection.getConfig()),
64
+ getConfig: Comlink.proxy(async () => {
65
+ // Can't send the logger over
66
+ const { logger, ...rest } = await connection.getConfig();
67
+ return rest;
68
+ }),
20
69
  close: Comlink.proxy(() => connection.close()),
21
70
  execute: Comlink.proxy(async (sql, params) => connection.execute(sql, params)),
22
71
  executeRaw: Comlink.proxy(async (sql, params) => connection.executeRaw(sql, params)),
@@ -27,15 +76,34 @@ const openWorkerConnection = async (options) => {
27
76
  })
28
77
  };
29
78
  };
30
- const openDBShared = async (options) => {
79
+ const openDBShared = async (options, logHandler) => {
31
80
  // Prevent multiple simultaneous opens from causing race conditions
32
81
  return getNavigatorLocks().request(OPEN_DB_LOCK, async () => {
33
82
  const clientId = nextClientId++;
34
83
  const { dbFilename, logLevel } = options;
35
- logger.setLevel(logLevel);
84
+ logToAll(`Worker opening DB connection for`, JSON.stringify(options));
85
+ // This updates the log level for the worker-level logger
86
+ // The DB connection logger will automatically track the main context logger
87
+ // since it passes logs to it.
88
+ workerLogger.setLevel(logLevel);
89
+ let disposeLogListener = logHandler
90
+ ? logBroadcaster.registerListener({
91
+ onLog: (event) => {
92
+ if (event.loggerName !== dbFilename) {
93
+ return;
94
+ }
95
+ logHandler(event);
96
+ }
97
+ })
98
+ : null;
36
99
  if (!DBMap.has(dbFilename)) {
37
100
  const clientIds = new Set();
38
- const connection = await openWorkerConnection(options);
101
+ const logger = createLogger(dbFilename);
102
+ logger.debug(`Worker opened a new connection`);
103
+ const connection = await openWorkerConnection({
104
+ ...options,
105
+ logger
106
+ });
39
107
  await connection.init();
40
108
  DBMap.set(dbFilename, {
41
109
  clientIds,
@@ -52,14 +120,15 @@ const openDBShared = async (options) => {
52
120
  }),
53
121
  close: Comlink.proxy(async () => {
54
122
  const { clientIds } = dbEntry;
55
- logger.debug(`Close requested from client ${clientId} of ${[...clientIds]}`);
123
+ disposeLogListener?.();
124
+ workerLogger.debug(`Close requested from client ${clientId} of ${[...clientIds]}`);
56
125
  clientIds.delete(clientId);
57
126
  if (clientIds.size == 0) {
58
- logger.debug(`Closing connection to ${dbFilename}.`);
127
+ workerLogger.debug(`Closing connection to ${dbFilename}.`);
59
128
  DBMap.delete(dbFilename);
60
129
  return db.close?.();
61
130
  }
62
- logger.debug(`Connection to ${dbFilename} not closed yet due to active clients.`);
131
+ workerLogger.debug(`Connection to ${dbFilename} not closed yet due to active clients.`);
63
132
  return;
64
133
  })
65
134
  };
@@ -78,9 +147,3 @@ else {
78
147
  // A dedicated worker can be shared externally
79
148
  Comlink.expose(openDBShared);
80
149
  }
81
- addEventListener('unload', () => {
82
- Array.from(DBMap.values()).forEach(async (dbConnection) => {
83
- const { db } = dbConnection;
84
- db.close?.();
85
- });
86
- });