@powersync/node 0.0.0-dev-20260503073249 → 0.0.0-dev-20260630141119

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 (73) hide show
  1. package/lib/attachments/NodeFileSystemAdapter.d.ts +1 -0
  2. package/lib/attachments/NodeFileSystemAdapter.d.ts.map +1 -0
  3. package/lib/db/AsyncDatabase.d.ts +8 -10
  4. package/lib/db/AsyncDatabase.d.ts.map +1 -0
  5. package/lib/db/BetterSqliteWorker.d.ts +6 -17
  6. package/lib/db/BetterSqliteWorker.d.ts.map +1 -0
  7. package/lib/db/BetterSqliteWorker.js +18 -9
  8. package/lib/db/BetterSqliteWorker.js.map +1 -1
  9. package/lib/db/DefaultWorker.d.ts +1 -0
  10. package/lib/db/DefaultWorker.d.ts.map +1 -0
  11. package/lib/db/NodeSqliteWorker.d.ts +6 -12
  12. package/lib/db/NodeSqliteWorker.d.ts.map +1 -0
  13. package/lib/db/NodeSqliteWorker.js +11 -9
  14. package/lib/db/NodeSqliteWorker.js.map +1 -1
  15. package/lib/db/PowerSyncDatabase.d.ts +7 -19
  16. package/lib/db/PowerSyncDatabase.d.ts.map +1 -0
  17. package/lib/db/PowerSyncDatabase.js +22 -29
  18. package/lib/db/PowerSyncDatabase.js.map +1 -1
  19. package/lib/db/RemoteConnection.d.ts +7 -18
  20. package/lib/db/RemoteConnection.d.ts.map +1 -0
  21. package/lib/db/RemoteConnection.js +10 -24
  22. package/lib/db/RemoteConnection.js.map +1 -1
  23. package/lib/db/SqliteWorker.d.ts +1 -0
  24. package/lib/db/SqliteWorker.d.ts.map +1 -0
  25. package/lib/db/SqliteWorker.js +3 -13
  26. package/lib/db/SqliteWorker.js.map +1 -1
  27. package/lib/db/WorkerConnectionPool.d.ts +5 -23
  28. package/lib/db/WorkerConnectionPool.d.ts.map +1 -0
  29. package/lib/db/WorkerConnectionPool.js +8 -22
  30. package/lib/db/WorkerConnectionPool.js.map +1 -1
  31. package/lib/db/options.d.ts +1 -0
  32. package/lib/db/options.d.ts.map +1 -0
  33. package/lib/index.d.ts +2 -0
  34. package/lib/index.d.ts.map +1 -0
  35. package/lib/index.js +1 -0
  36. package/lib/index.js.map +1 -1
  37. package/lib/sync/stream/NodeRemote.d.ts +15 -8
  38. package/lib/sync/stream/NodeRemote.d.ts.map +1 -0
  39. package/lib/sync/stream/NodeRemote.js +26 -21
  40. package/lib/sync/stream/NodeRemote.js.map +1 -1
  41. package/lib/sync/stream/NodeStreamingSyncImplementation.d.ts +2 -1
  42. package/lib/sync/stream/NodeStreamingSyncImplementation.d.ts.map +1 -0
  43. package/lib/sync/stream/NodeStreamingSyncImplementation.js +2 -2
  44. package/lib/sync/stream/NodeStreamingSyncImplementation.js.map +1 -1
  45. package/lib/worker.d.ts +1 -0
  46. package/lib/worker.d.ts.map +1 -0
  47. package/package.json +19 -30
  48. package/src/db/AsyncDatabase.ts +7 -10
  49. package/src/db/BetterSqliteWorker.ts +22 -14
  50. package/src/db/NodeSqliteWorker.ts +16 -13
  51. package/src/db/PowerSyncDatabase.ts +46 -59
  52. package/src/db/RemoteConnection.ts +12 -34
  53. package/src/db/SqliteWorker.ts +3 -13
  54. package/src/db/WorkerConnectionPool.ts +16 -33
  55. package/src/index.ts +2 -1
  56. package/src/sync/stream/NodeRemote.ts +43 -44
  57. package/src/sync/stream/NodeStreamingSyncImplementation.ts +2 -2
  58. package/dist/DefaultWorker.cjs +0 -232
  59. package/dist/DefaultWorker.cjs.map +0 -1
  60. package/dist/bundle.cjs +0 -533
  61. package/dist/bundle.cjs.map +0 -1
  62. package/dist/bundle.d.cts +0 -127
  63. package/dist/worker.cjs +0 -233
  64. package/dist/worker.cjs.map +0 -1
  65. package/dist/worker.d.cts +0 -20
  66. package/lib/utils/modules.d.ts +0 -2
  67. package/lib/utils/modules.js +0 -6
  68. package/lib/utils/modules.js.map +0 -1
  69. package/lib/utils/modules_commonjs.d.ts +0 -2
  70. package/lib/utils/modules_commonjs.js +0 -7
  71. package/lib/utils/modules_commonjs.js.map +0 -1
  72. package/src/utils/modules.ts +0 -6
  73. package/src/utils/modules_commonjs.ts +0 -7
@@ -1,91 +1,59 @@
1
1
  import {
2
- AbstractPowerSyncDatabase,
3
- AbstractRemoteOptions,
4
- AbstractStreamingSyncImplementation,
5
- AdditionalConnectionOptions,
6
- BucketStorageAdapter,
2
+ BasePowerSyncDatabaseOptions,
3
+ CommonPowerSyncDatabase,
4
+ DatabaseSource,
7
5
  DBAdapter,
8
6
  PowerSyncBackendConnector,
9
- PowerSyncConnectionOptions,
10
- PowerSyncDatabaseOptions,
11
- PowerSyncDatabaseOptionsWithSettings,
12
- RequiredAdditionalConnectionOptions,
13
- SqliteBucketStorage,
14
- SQLOpenFactory
7
+ PowerSyncDatabaseConstructor
15
8
  } from '@powersync/common';
9
+ import {
10
+ BasePowerSyncDatabase,
11
+ AbstractStreamingSyncImplementation,
12
+ BucketStorageAdapter,
13
+ CreateSyncImplementationOptions,
14
+ SqliteBucketStorage,
15
+ openDatabase
16
+ } from '@powersync/shared-internals';
16
17
 
17
- import { NodeCustomConnectionOptions, NodeRemote } from '../sync/stream/NodeRemote.js';
18
+ import { NodeRemote, NodeRemoteOptions } from '../sync/stream/NodeRemote.js';
18
19
  import { NodeStreamingSyncImplementation } from '../sync/stream/NodeStreamingSyncImplementation.js';
19
20
 
20
- import { WorkerPoolDatabaseAdapter } from './WorkerConnectionPool.js';
21
+ import { WorkerConnectionPool } from './WorkerConnectionPool.js';
21
22
  import { NodeSQLOpenOptions } from './options.js';
22
23
 
23
- export type NodePowerSyncDatabaseOptions = PowerSyncDatabaseOptions & {
24
- database: DBAdapter | SQLOpenFactory | NodeSQLOpenOptions;
25
- /**
26
- * Options to override how the SDK will connect to the sync service.
27
- *
28
- * This option is intended to be used for internal tests.
29
- */
30
- remoteOptions?: Partial<AbstractRemoteOptions>;
31
- };
32
-
33
- export type NodeAdditionalConnectionOptions = AdditionalConnectionOptions & NodeCustomConnectionOptions;
34
-
35
- export type NodePowerSyncConnectionOptions = PowerSyncConnectionOptions & NodeAdditionalConnectionOptions;
24
+ export type NodePowerSyncDatabaseOptions = BasePowerSyncDatabaseOptions &
25
+ DatabaseSource<NodeSQLOpenOptions> & {
26
+ /**
27
+ * Options to override how the SDK will connect to the sync service.
28
+ */
29
+ remoteOptions?: NodeRemoteOptions;
30
+ };
36
31
 
37
- /**
38
- * A PowerSync database which provides SQLite functionality
39
- * which is automatically synced.
40
- *
41
- * @example
42
- * ```typescript
43
- * export const db = new PowerSyncDatabase({
44
- * schema: AppSchema,
45
- * database: {
46
- * dbFilename: 'example.db'
47
- * }
48
- * });
49
- * ```
50
- */
51
- export class PowerSyncDatabase extends AbstractPowerSyncDatabase {
32
+ class NodePowerSyncDatabase extends BasePowerSyncDatabase<NodePowerSyncDatabaseOptions> {
52
33
  constructor(options: NodePowerSyncDatabaseOptions) {
53
34
  super(options);
54
35
  }
55
36
 
56
37
  async _initialize(): Promise<void> {
57
38
  if ('initialize' in this.database) {
58
- await (this.database as WorkerPoolDatabaseAdapter).initialize();
39
+ await (this.database as WorkerConnectionPool).initialize();
59
40
  }
60
41
  }
61
42
 
62
- /**
63
- * Opens a DBAdapter using better-sqlite3 as the default SQLite open factory.
64
- */
65
- protected openDBAdapter(options: PowerSyncDatabaseOptionsWithSettings): DBAdapter {
66
- return new WorkerPoolDatabaseAdapter(options.database);
43
+ protected openDBAdapter(): DBAdapter {
44
+ return openDatabase(this.options, (open) => new WorkerConnectionPool(open));
67
45
  }
68
46
 
69
47
  protected generateBucketStorageAdapter(): BucketStorageAdapter {
70
48
  return new SqliteBucketStorage(this.database, this.logger);
71
49
  }
72
50
 
73
- connect(
74
- connector: PowerSyncBackendConnector,
75
- options?: PowerSyncConnectionOptions & NodeCustomConnectionOptions
76
- ): Promise<void> {
77
- return super.connect(connector, options);
78
- }
79
-
80
51
  protected generateSyncStreamImplementation(
81
52
  connector: PowerSyncBackendConnector,
82
- options: RequiredAdditionalConnectionOptions & NodeAdditionalConnectionOptions
53
+ options: CreateSyncImplementationOptions
83
54
  ): AbstractStreamingSyncImplementation {
84
55
  const logger = this.logger;
85
- const remote = new NodeRemote(connector, logger, {
86
- dispatcher: options.dispatcher,
87
- ...(this.options as NodePowerSyncDatabaseOptions).remoteOptions
88
- });
56
+ const remote = new NodeRemote(connector, logger, this.options.remoteOptions);
89
57
 
90
58
  return new NodeStreamingSyncImplementation({
91
59
  adapter: this.bucketStorageAdapter,
@@ -100,3 +68,22 @@ export class PowerSyncDatabase extends AbstractPowerSyncDatabase {
100
68
  });
101
69
  }
102
70
  }
71
+
72
+ /**
73
+ * A PowerSync database which provides SQLite functionality
74
+ * which is automatically synced.
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * export const db = new PowerSyncDatabase({
79
+ * schema: AppSchema,
80
+ * database: {
81
+ * dbFilename: 'example.db'
82
+ * }
83
+ * });
84
+ * ```
85
+ */
86
+ // Typed constructor to avoid leaking AbstractPowerSyncDatabase into the public interface
87
+ export const PowerSyncDatabase: PowerSyncDatabaseConstructor<NodePowerSyncDatabaseOptions> = NodePowerSyncDatabase;
88
+
89
+ export interface PowerSyncDatabase extends CommonPowerSyncDatabase {}
@@ -1,18 +1,13 @@
1
- import {
2
- ConnectionClosedError,
3
- DBGetUtilsDefaultMixin,
4
- QueryResult,
5
- SqlExecutor,
6
- LockContext
7
- } from '@powersync/common';
1
+ import { QueryResult, LockContext, RawQueryResult, SqliteRecord, queryResultFromMapped } from '@powersync/common';
8
2
  import { releaseProxy, Remote } from 'comlink';
9
3
  import { Worker } from 'node:worker_threads';
10
- import { AsyncDatabase, AsyncDatabaseOpener, ProxiedQueryResult } from './AsyncDatabase.js';
4
+ import { AsyncDatabase, AsyncDatabaseOpener } from './AsyncDatabase.js';
5
+ import { ConnectionClosedError } from '@powersync/shared-internals';
11
6
 
12
7
  /**
13
8
  * A PowerSync database connection implemented with RPC calls to a background worker.
14
9
  */
15
- class BaseRemoteConnection implements SqlExecutor {
10
+ export class RemoteConnection extends LockContext {
16
11
  private readonly worker: Worker;
17
12
  private readonly comlink: Remote<AsyncDatabaseOpener>;
18
13
  private readonly database: Remote<AsyncDatabase>;
@@ -20,6 +15,7 @@ class BaseRemoteConnection implements SqlExecutor {
20
15
  private readonly notifyWorkerClosed = new AbortController();
21
16
 
22
17
  constructor(worker: Worker, comlink: Remote<AsyncDatabaseOpener>, database: Remote<AsyncDatabase>) {
18
+ super();
23
19
  this.worker = worker;
24
20
  this.comlink = comlink;
25
21
  this.database = database;
@@ -66,28 +62,27 @@ class BaseRemoteConnection implements SqlExecutor {
66
62
  });
67
63
  }
68
64
 
69
- executeBatch(query: string, params: any[][] = []): Promise<QueryResult> {
65
+ execute<T = SqliteRecord>(query: string, params?: any[] | undefined): Promise<QueryResult<T>> {
70
66
  return this.withRemote(async () => {
71
- const result = await this.database.executeBatch(query, params ?? []);
72
- return BaseRemoteConnection.wrapQueryResult(result);
67
+ const results = await this.database.execute(query, params ?? []);
68
+ return queryResultFromMapped(results, results.rows as T[] | undefined);
73
69
  });
74
70
  }
75
71
 
76
- execute(query: string, params?: any[] | undefined): Promise<QueryResult> {
72
+ executeBatch(query: string, params: any[][] = []): Promise<QueryResult<never>> {
77
73
  return this.withRemote(async () => {
78
- const result = await this.database.execute(query, params ?? []);
79
- return BaseRemoteConnection.wrapQueryResult(result);
74
+ return await this.database.executeBatch(query, params ?? []);
80
75
  });
81
76
  }
82
77
 
83
- executeRaw(query: string, params?: any[] | undefined): Promise<any[][]> {
78
+ executeRaw(query: string, params?: any[] | undefined): Promise<RawQueryResult> {
84
79
  return this.withRemote(async () => {
85
80
  return await this.database.executeRaw(query, params ?? []);
86
81
  });
87
82
  }
88
83
 
89
84
  async refreshSchema() {
90
- await this.execute("pragma table_info('sqlite_master')");
85
+ await this.executeRaw("pragma table_info('sqlite_master')");
91
86
  }
92
87
 
93
88
  async close() {
@@ -96,21 +91,4 @@ class BaseRemoteConnection implements SqlExecutor {
96
91
  this.comlink[releaseProxy]();
97
92
  await this.worker.terminate();
98
93
  }
99
-
100
- static wrapQueryResult(result: ProxiedQueryResult): QueryResult {
101
- let rows: QueryResult['rows'] | undefined = undefined;
102
- if (result.rows) {
103
- rows = {
104
- ...result.rows,
105
- item: (idx) => result.rows?._array[idx]
106
- } satisfies QueryResult['rows'];
107
- }
108
-
109
- return {
110
- ...result,
111
- rows
112
- };
113
- }
114
94
  }
115
-
116
- export class RemoteConnection extends DBGetUtilsDefaultMixin(BaseRemoteConnection) implements LockContext {}
@@ -1,9 +1,7 @@
1
1
  import * as Comlink from 'comlink';
2
2
  import OS from 'node:os';
3
- import * as path from 'node:path';
4
3
  import url from 'node:url';
5
4
  import { parentPort } from 'node:worker_threads';
6
- import { dynamicImport, isBundledToCommonJs } from '../utils/modules.js';
7
5
  import { AsyncDatabase, AsyncDatabaseOpenOptions, AsyncDatabaseOpener } from './AsyncDatabase.js';
8
6
  import { openDatabase as openBetterSqliteDatabase } from './BetterSqliteWorker.js';
9
7
  import { openDatabase as openNodeDatabase } from './NodeSqliteWorker.js';
@@ -71,21 +69,13 @@ export function getPowerSyncExtensionFilename() {
71
69
  export function startPowerSyncWorker(options?: Partial<PowerSyncWorkerOptions>) {
72
70
  const resolvedOptions: PowerSyncWorkerOptions = {
73
71
  extensionPath() {
74
- const isCommonJsModule = isBundledToCommonJs;
75
72
  const extensionFilename = getPowerSyncExtensionFilename();
76
- let resolved: string;
77
- if (isCommonJsModule) {
78
- resolved = path.resolve(__dirname, '../lib/', extensionFilename);
79
- } else {
80
- resolved = url.fileURLToPath(new URL(`../${extensionFilename}`, import.meta.url));
81
- }
82
-
73
+ const resolved = url.fileURLToPath(new URL(`../${extensionFilename}`, import.meta.url));
83
74
  return resolved;
84
75
  },
85
76
  async loadBetterSqlite3() {
86
- const module = await dynamicImport('better-sqlite3');
87
- // require() gives us the default directly, for an ESM import() we need to use the default export.
88
- return isBundledToCommonJs ? module : module.default;
77
+ const module = await import('better-sqlite3');
78
+ return module.default;
89
79
  },
90
80
  ...options
91
81
  };
@@ -4,20 +4,15 @@ import * as path from 'node:path';
4
4
  import { Worker } from 'node:worker_threads';
5
5
 
6
6
  import {
7
- BaseObserver,
8
7
  BatchedUpdateNotification,
9
- ConnectionPool,
10
- DBAdapterDefaultMixin,
11
- DBAdapterListener,
8
+ DBAdapter,
12
9
  DBLockOptions,
13
10
  LockContext,
14
11
  QueryResult,
15
- Semaphore,
16
- timeoutSignal,
17
12
  Transaction
18
13
  } from '@powersync/common';
14
+ import { Semaphore, timeoutSignal } from '@powersync/shared-internals';
19
15
  import { Remote } from 'comlink';
20
- import { isBundledToCommonJs } from '../utils/modules.js';
21
16
  import { AsyncDatabase, AsyncDatabaseOpener } from './AsyncDatabase.js';
22
17
  import { RemoteConnection } from './RemoteConnection.js';
23
18
  import { NodeDatabaseImplementation, NodeSQLOpenOptions } from './options.js';
@@ -37,12 +32,12 @@ const defaultDatabaseImplementation: NodeDatabaseImplementation = {
37
32
  /**
38
33
  * Adapter for better-sqlite3
39
34
  */
40
- export class WorkerConnectionPool extends BaseObserver<DBAdapterListener> implements ConnectionPool {
35
+ export class WorkerConnectionPool extends DBAdapter {
41
36
  private readonly options: NodeSQLOpenOptions;
42
37
  public readonly name: string;
43
38
 
44
- private writeConnection: Semaphore<RemoteConnection>;
45
- private readConnections: Semaphore<RemoteConnection>;
39
+ private writeConnection?: Semaphore<RemoteConnection>;
40
+ private readConnections?: Semaphore<RemoteConnection>;
46
41
 
47
42
  constructor(options: NodeSQLOpenOptions) {
48
43
  super();
@@ -77,21 +72,15 @@ export class WorkerConnectionPool extends BaseObserver<DBAdapterListener> implem
77
72
  }
78
73
 
79
74
  const openWorker = async (isWriter: boolean) => {
80
- const isCommonJsModule = isBundledToCommonJs;
81
- let worker: Worker;
82
75
  const workerName = isWriter ? `write ${dbFilePath}` : `read ${dbFilePath}`;
83
76
 
84
77
  const workerFactory = this.options.openWorker ?? ((...args) => new Worker(...args));
85
- if (isCommonJsModule) {
86
- worker = workerFactory(path.resolve(__dirname, 'DefaultWorker.cjs'), { name: workerName });
87
- } else {
88
- worker = workerFactory(new URL('./DefaultWorker.js', import.meta.url), { name: workerName });
89
- }
78
+ const worker = workerFactory(new URL('./DefaultWorker.js', import.meta.url), { name: workerName });
90
79
 
91
80
  const listeners = new WeakMap<EventListenerOrEventListenerObject, (e: any) => void>();
92
81
 
93
82
  const comlink = Comlink.wrap<AsyncDatabaseOpener>({
94
- postMessage: worker.postMessage.bind(worker),
83
+ postMessage: worker.postMessage.bind(worker) as any,
95
84
  addEventListener: (type, listener) => {
96
85
  let resolved: (event: any) => void =
97
86
  'handleEvent' in listener ? listener.handleEvent.bind(listener) : listener;
@@ -127,16 +116,14 @@ export class WorkerConnectionPool extends BaseObserver<DBAdapterListener> implem
127
116
  implementation: this.options.implementation ?? defaultDatabaseImplementation
128
117
  })) as Remote<AsyncDatabase>;
129
118
  if (isWriter) {
130
- await database.execute("SELECT powersync_update_hooks('install');", []);
119
+ await database.executeRaw("SELECT powersync_update_hooks('install');", []);
131
120
  }
132
121
 
133
122
  const connection = new RemoteConnection(worker, comlink, database);
134
123
  if (this.options.initializeConnection) {
135
124
  await this.options.initializeConnection(connection, isWriter);
136
125
  }
137
- if (!isWriter) {
138
- await connection.execute('pragma query_only = true');
139
- } else {
126
+ if (isWriter) {
140
127
  // We only need to enable this on the writer connection.
141
128
  // We can get `database is locked` errors if we enable this on concurrently opening read connections.
142
129
  await connection.execute('pragma journal_mode = WAL');
@@ -156,8 +143,8 @@ export class WorkerConnectionPool extends BaseObserver<DBAdapterListener> implem
156
143
  }
157
144
 
158
145
  async close() {
159
- const { item: writeConnection, release: returnWrite } = await this.writeConnection.requestOne();
160
- const { items: readers, release: returnReaders } = await this.readConnections.requestAll();
146
+ const { item: writeConnection, release: returnWrite } = await this.writeConnection!.requestOne();
147
+ const { items: readers, release: returnReaders } = await this.readConnections!.requestAll();
161
148
 
162
149
  try {
163
150
  await writeConnection.close();
@@ -169,7 +156,7 @@ export class WorkerConnectionPool extends BaseObserver<DBAdapterListener> implem
169
156
  }
170
157
 
171
158
  async readLock<T>(fn: (tx: RemoteConnection) => Promise<T>, options?: DBLockOptions | undefined): Promise<T> {
172
- const lease = await this.readConnections.requestOne(timeoutSignal(options?.timeoutMs));
159
+ const lease = await this.readConnections!.requestOne(timeoutSignal(options?.timeoutMs));
173
160
  try {
174
161
  return await fn(lease.item);
175
162
  } finally {
@@ -178,20 +165,18 @@ export class WorkerConnectionPool extends BaseObserver<DBAdapterListener> implem
178
165
  }
179
166
 
180
167
  async writeLock<T>(fn: (tx: RemoteConnection) => Promise<T>, options?: DBLockOptions | undefined): Promise<T> {
181
- const { item, release } = await this.writeConnection.requestOne(timeoutSignal(options?.timeoutMs));
168
+ const { item, release } = await this.writeConnection!.requestOne(timeoutSignal(options?.timeoutMs));
182
169
 
183
170
  try {
184
171
  try {
185
172
  return await fn(item);
186
173
  } finally {
187
- const serializedUpdates = await item.executeRaw("SELECT powersync_update_hooks('get');", []);
174
+ const { rawRows: serializedUpdates } = await item.executeRaw("SELECT powersync_update_hooks('get');", []);
188
175
  const updates = JSON.parse(serializedUpdates[0][0] as string) as string[];
189
176
 
190
177
  if (updates.length > 0) {
191
178
  const event: BatchedUpdateNotification = {
192
- tables: updates,
193
- groupedUpdates: {},
194
- rawUpdates: []
179
+ tables: updates
195
180
  };
196
181
  this.iterateListeners((cb) => cb.tablesUpdated?.(event));
197
182
  }
@@ -204,7 +189,7 @@ export class WorkerConnectionPool extends BaseObserver<DBAdapterListener> implem
204
189
  async refreshSchema() {
205
190
  await this.writeLock((l) => l.refreshSchema());
206
191
 
207
- const { items, release } = await this.readConnections.requestAll();
192
+ const { items, release } = await this.readConnections!.requestAll();
208
193
  try {
209
194
  await Promise.all(items.map((c) => c.refreshSchema()));
210
195
  } finally {
@@ -212,5 +197,3 @@ export class WorkerConnectionPool extends BaseObserver<DBAdapterListener> implem
212
197
  }
213
198
  }
214
199
  }
215
-
216
- export class WorkerPoolDatabaseAdapter extends DBAdapterDefaultMixin(WorkerConnectionPool) {}
package/src/index.ts CHANGED
@@ -2,5 +2,6 @@
2
2
  export * from '@powersync/common';
3
3
 
4
4
  export * from './db/PowerSyncDatabase.js';
5
+ export * from './db/options.js';
5
6
 
6
- export * from './attachments/NodeFileSystemAdapter.js';
7
+ export * from './attachments/NodeFileSystemAdapter.js';
@@ -1,34 +1,18 @@
1
1
  import * as os from 'node:os';
2
2
 
3
- import {
4
- type ILogger,
5
- AbstractRemote,
6
- AbstractRemoteOptions,
7
- BSONImplementation,
8
- DEFAULT_REMOTE_LOGGER,
9
- FetchImplementation,
10
- FetchImplementationProvider,
11
- RemoteConnector
12
- } from '@powersync/common';
13
- import { BSON } from 'bson';
14
- import {
15
- Dispatcher,
16
- EnvHttpProxyAgent,
17
- ErrorEvent,
18
- getGlobalDispatcher,
19
- ProxyAgent,
20
- WebSocket as UndiciWebSocket
21
- } from 'undici';
3
+ import { AbstractRemote, FetchOptions, RemoteConnector } from '@powersync/shared-internals';
4
+ import { Dispatcher, EnvHttpProxyAgent, getGlobalDispatcher, ProxyAgent, WebSocket as UndiciWebSocket } from 'undici';
5
+ import { PowerSyncLogger } from '@powersync/common';
6
+ import type { WebSocketSyncStreamPlatform, WebSocketSupport } from '@powersync/shared-internals/websockets';
22
7
 
23
8
  export const STREAMING_POST_TIMEOUT_MS = 30_000;
24
9
 
25
- class NodeFetchProvider extends FetchImplementationProvider {
26
- getFetch(): FetchImplementation {
27
- return fetch.bind(globalThis);
28
- }
29
- }
10
+ export interface NodeRemoteOptions {
11
+ /**
12
+ * @internal Only meant to be used for tests.
13
+ */
14
+ customFetch?: typeof fetch;
30
15
 
31
- export type NodeCustomConnectionOptions = {
32
16
  /**
33
17
  * Optional custom dispatcher for HTTP or WEB_SOCKET connections.
34
18
  *
@@ -36,32 +20,49 @@ export type NodeCustomConnectionOptions = {
36
20
  * or other connection options.
37
21
  */
38
22
  dispatcher?: Dispatcher;
39
- };
40
-
41
- export type NodeRemoteOptions = AbstractRemoteOptions & NodeCustomConnectionOptions;
23
+ }
42
24
 
43
25
  export class NodeRemote extends AbstractRemote {
44
26
  private wsDispatcher: Dispatcher | undefined;
27
+ private fetchImpl: typeof fetch;
45
28
 
46
29
  constructor(
47
30
  protected connector: RemoteConnector,
48
- protected logger: ILogger = DEFAULT_REMOTE_LOGGER,
49
- options?: Partial<NodeRemoteOptions>
31
+ logger: PowerSyncLogger,
32
+ options?: NodeRemoteOptions
50
33
  ) {
51
- const fetchDispatcher = options?.dispatcher ?? defaultFetchDispatcher();
52
-
53
- super(connector, logger, {
54
- fetchImplementation: options?.fetchImplementation ?? new NodeFetchProvider(),
55
- fetchOptions: {
56
- dispatcher: fetchDispatcher
57
- },
58
- ...(options ?? {})
59
- });
34
+ super(connector, logger);
35
+
36
+ this.fetchImpl =
37
+ options?.customFetch ??
38
+ ((resource, init) => {
39
+ const dispatcher = options?.dispatcher ?? defaultFetchDispatcher();
40
+
41
+ return fetch(resource, {
42
+ // @ts-expect-error
43
+ dispatcher,
44
+ ...init
45
+ });
46
+ });
60
47
 
61
48
  this.wsDispatcher = options?.dispatcher;
62
49
  }
63
50
 
64
- protected createSocket(url: string): globalThis.WebSocket {
51
+ protected fetch({ resource, request }: FetchOptions): Promise<Response> {
52
+ return this.fetchImpl(resource, request);
53
+ }
54
+
55
+ protected async loadWebSocketSupport(platform: WebSocketSyncStreamPlatform): Promise<WebSocketSupport> {
56
+ if (!websockets) {
57
+ // loadWebSocketSupport being called concurrently is safe, the import resolves to the same module in that case.
58
+ const module = await import('@powersync/shared-internals/websockets');
59
+ websockets = new module.WebSocketSupport(platform);
60
+ }
61
+
62
+ return websockets;
63
+ }
64
+
65
+ createSocket(url: string): globalThis.WebSocket {
65
66
  // Create dedicated dispatcher for this WebSocket
66
67
  const baseDispatcher = this.getWebsocketDispatcher(url);
67
68
 
@@ -98,10 +99,6 @@ export class NodeRemote extends AbstractRemote {
98
99
  `${os.platform()}/${os.release()}`
99
100
  ].join(' ');
100
101
  }
101
-
102
- async getBSON(): Promise<BSONImplementation> {
103
- return BSON;
104
- }
105
102
  }
106
103
 
107
104
  function defaultFetchDispatcher(): Dispatcher {
@@ -121,3 +118,5 @@ function getProxyForProtocol(protocol: string): string | undefined {
121
118
  process.env[`ALL_PROXY`]
122
119
  );
123
120
  }
121
+
122
+ let websockets: WebSocketSupport | undefined;
@@ -4,7 +4,7 @@ import {
4
4
  LockOptions,
5
5
  LockType,
6
6
  Mutex
7
- } from '@powersync/common';
7
+ } from '@powersync/shared-internals';
8
8
 
9
9
  /**
10
10
  * Global locks which prevent multiple instances from syncing
@@ -13,7 +13,7 @@ import {
13
13
  const LOCKS = new Map<string, Map<LockType, Mutex>>();
14
14
 
15
15
  export class NodeStreamingSyncImplementation extends AbstractStreamingSyncImplementation {
16
- locks: Map<LockType, Mutex>;
16
+ locks!: Map<LockType, Mutex>; // initialized by initLocks()
17
17
 
18
18
  constructor(options: AbstractStreamingSyncImplementationOptions) {
19
19
  super(options);