@powersync/common 1.57.1 → 2.0.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 (219) hide show
  1. package/lib/attachments/AttachmentContext.d.ts +5 -5
  2. package/lib/attachments/AttachmentContext.js +6 -2
  3. package/lib/attachments/AttachmentContext.js.map +1 -1
  4. package/lib/attachments/AttachmentQueue.d.ts +6 -6
  5. package/lib/attachments/AttachmentQueue.js +9 -8
  6. package/lib/attachments/AttachmentQueue.js.map +1 -1
  7. package/lib/attachments/AttachmentService.d.ts +3 -3
  8. package/lib/attachments/AttachmentService.js +4 -3
  9. package/lib/attachments/AttachmentService.js.map +1 -1
  10. package/lib/attachments/Schema.d.ts +2 -2
  11. package/lib/attachments/SyncingService.d.ts +2 -2
  12. package/lib/attachments/SyncingService.js +21 -6
  13. package/lib/attachments/SyncingService.js.map +1 -1
  14. package/lib/client/{AbstractPowerSyncDatabase.d.ts → CommonPowerSyncDatabase.d.ts} +56 -232
  15. package/lib/client/CommonPowerSyncDatabase.js +2 -0
  16. package/lib/client/CommonPowerSyncDatabase.js.map +1 -0
  17. package/lib/client/SQLOpenFactory.d.ts +35 -13
  18. package/lib/client/SQLOpenFactory.js +1 -25
  19. package/lib/client/SQLOpenFactory.js.map +1 -1
  20. package/lib/client/compilableQueryWatch.d.ts +2 -2
  21. package/lib/client/compilableQueryWatch.js +1 -2
  22. package/lib/client/compilableQueryWatch.js.map +1 -1
  23. package/lib/client/connection/PowerSyncBackendConnector.d.ts +3 -3
  24. package/lib/client/runOnSchemaChange.d.ts +2 -2
  25. package/lib/client/runOnSchemaChange.js.map +1 -1
  26. package/lib/client/sync/bucket/CrudEntry.d.ts +3 -34
  27. package/lib/client/sync/bucket/CrudEntry.js +0 -99
  28. package/lib/client/sync/bucket/CrudEntry.js.map +1 -1
  29. package/lib/client/sync/options.d.ts +65 -0
  30. package/lib/client/sync/options.js +25 -0
  31. package/lib/client/sync/options.js.map +1 -0
  32. package/lib/client/sync/sync-streams.d.ts +1 -1
  33. package/lib/client/triggers/TriggerManager.d.ts +9 -38
  34. package/lib/client/watched/GetAllQuery.d.ts +3 -3
  35. package/lib/client/watched/GetAllQuery.js +1 -1
  36. package/lib/client/watched/GetAllQuery.js.map +1 -1
  37. package/lib/client/watched/WatchedQuery.d.ts +2 -10
  38. package/lib/client/watched/WatchedQuery.js +0 -11
  39. package/lib/client/watched/WatchedQuery.js.map +1 -1
  40. package/lib/client/watched/processors/DifferentialQueryProcessor.d.ts +0 -50
  41. package/lib/client/watched/processors/DifferentialQueryProcessor.js +1 -176
  42. package/lib/client/watched/processors/DifferentialQueryProcessor.js.map +1 -1
  43. package/lib/client/watched/processors/OnChangeQueryProcessor.d.ts +0 -22
  44. package/lib/client/watched/processors/OnChangeQueryProcessor.js +1 -82
  45. package/lib/client/watched/processors/OnChangeQueryProcessor.js.map +1 -1
  46. package/lib/db/DBAdapter.d.ts +72 -154
  47. package/lib/db/DBAdapter.js +70 -108
  48. package/lib/db/DBAdapter.js.map +1 -1
  49. package/lib/db/QueryResult.d.ts +104 -0
  50. package/lib/db/QueryResult.js +96 -0
  51. package/lib/db/QueryResult.js.map +1 -0
  52. package/lib/db/crud/SyncProgress.d.ts +1 -13
  53. package/lib/db/crud/SyncProgress.js +1 -62
  54. package/lib/db/crud/SyncProgress.js.map +1 -1
  55. package/lib/db/crud/SyncStatus.d.ts +32 -66
  56. package/lib/db/crud/SyncStatus.js +1 -253
  57. package/lib/db/crud/SyncStatus.js.map +1 -1
  58. package/lib/db/schema/Column.d.ts +0 -7
  59. package/lib/db/schema/Column.js +0 -7
  60. package/lib/db/schema/Column.js.map +1 -1
  61. package/lib/db/schema/Index.d.ts +2 -6
  62. package/lib/db/schema/Index.js +1 -4
  63. package/lib/db/schema/Index.js.map +1 -1
  64. package/lib/db/schema/IndexedColumn.d.ts +2 -6
  65. package/lib/db/schema/IndexedColumn.js +1 -4
  66. package/lib/db/schema/IndexedColumn.js.map +1 -1
  67. package/lib/db/schema/Schema.d.ts +4 -28
  68. package/lib/db/schema/Schema.js.map +1 -1
  69. package/lib/db/schema/Table.d.ts +50 -96
  70. package/lib/db/schema/Table.js +83 -91
  71. package/lib/db/schema/Table.js.map +1 -1
  72. package/lib/index.d.ts +5 -21
  73. package/lib/index.js +5 -22
  74. package/lib/index.js.map +1 -1
  75. package/lib/utils/BaseObserver.d.ts +1 -1
  76. package/lib/utils/BaseObserver.js +1 -1
  77. package/lib/utils/Logger.d.ts +52 -23
  78. package/lib/utils/Logger.js +35 -33
  79. package/lib/utils/Logger.js.map +1 -1
  80. package/lib/utils/MetaBaseObserver.d.ts +11 -11
  81. package/lib/utils/MetaBaseObserver.js +1 -50
  82. package/lib/utils/MetaBaseObserver.js.map +1 -1
  83. package/lib/utils/mutex.d.ts +3 -53
  84. package/lib/utils/mutex.js +1 -156
  85. package/lib/utils/mutex.js.map +1 -1
  86. package/package.json +8 -45
  87. package/src/attachments/AttachmentContext.ts +11 -10
  88. package/src/attachments/AttachmentQueue.ts +15 -14
  89. package/src/attachments/AttachmentService.ts +7 -6
  90. package/src/attachments/Schema.ts +2 -2
  91. package/src/attachments/SyncingService.ts +23 -9
  92. package/src/client/CommonPowerSyncDatabase.ts +540 -0
  93. package/src/client/SQLOpenFactory.ts +38 -22
  94. package/src/client/compilableQueryWatch.ts +3 -4
  95. package/src/client/connection/PowerSyncBackendConnector.ts +3 -3
  96. package/src/client/runOnSchemaChange.ts +2 -2
  97. package/src/client/sync/bucket/CrudEntry.ts +4 -104
  98. package/src/client/sync/options.ts +77 -0
  99. package/src/client/sync/sync-streams.ts +1 -1
  100. package/src/client/triggers/TriggerManager.ts +10 -41
  101. package/src/client/watched/GetAllQuery.ts +3 -3
  102. package/src/client/watched/WatchedQuery.ts +2 -15
  103. package/src/client/watched/processors/DifferentialQueryProcessor.ts +0 -223
  104. package/src/client/watched/processors/OnChangeQueryProcessor.ts +0 -111
  105. package/src/db/DBAdapter.ts +141 -226
  106. package/src/db/QueryResult.ts +195 -0
  107. package/src/db/crud/SyncProgress.ts +2 -42
  108. package/src/db/crud/SyncStatus.ts +45 -220
  109. package/src/db/schema/Column.ts +0 -8
  110. package/src/db/schema/Index.ts +3 -6
  111. package/src/db/schema/IndexedColumn.ts +3 -6
  112. package/src/db/schema/Schema.ts +4 -4
  113. package/src/db/schema/Table.ts +111 -186
  114. package/src/index.ts +5 -21
  115. package/src/utils/BaseObserver.ts +1 -1
  116. package/src/utils/Logger.ts +80 -34
  117. package/src/utils/MetaBaseObserver.ts +14 -60
  118. package/src/utils/mutex.ts +4 -201
  119. package/dist/bundle.cjs +0 -14390
  120. package/dist/bundle.cjs.map +0 -1
  121. package/dist/bundle.mjs +0 -14309
  122. package/dist/bundle.mjs.map +0 -1
  123. package/dist/bundle.node.cjs +0 -12021
  124. package/dist/bundle.node.cjs.map +0 -1
  125. package/dist/bundle.node.mjs +0 -11940
  126. package/dist/bundle.node.mjs.map +0 -1
  127. package/dist/index.d.cts +0 -4651
  128. package/legacy/sync_protocol.d.ts +0 -103
  129. package/lib/client/AbstractPowerSyncDatabase.js +0 -990
  130. package/lib/client/AbstractPowerSyncDatabase.js.map +0 -1
  131. package/lib/client/AbstractPowerSyncOpenFactory.d.ts +0 -28
  132. package/lib/client/AbstractPowerSyncOpenFactory.js +0 -28
  133. package/lib/client/AbstractPowerSyncOpenFactory.js.map +0 -1
  134. package/lib/client/ConnectionManager.d.ts +0 -115
  135. package/lib/client/ConnectionManager.js +0 -294
  136. package/lib/client/ConnectionManager.js.map +0 -1
  137. package/lib/client/CustomQuery.d.ts +0 -22
  138. package/lib/client/CustomQuery.js +0 -43
  139. package/lib/client/CustomQuery.js.map +0 -1
  140. package/lib/client/constants.d.ts +0 -4
  141. package/lib/client/constants.js +0 -5
  142. package/lib/client/constants.js.map +0 -1
  143. package/lib/client/sync/bucket/BucketStorageAdapter.d.ts +0 -56
  144. package/lib/client/sync/bucket/BucketStorageAdapter.js +0 -29
  145. package/lib/client/sync/bucket/BucketStorageAdapter.js.map +0 -1
  146. package/lib/client/sync/bucket/SqliteBucketStorage.d.ts +0 -37
  147. package/lib/client/sync/bucket/SqliteBucketStorage.js +0 -165
  148. package/lib/client/sync/bucket/SqliteBucketStorage.js.map +0 -1
  149. package/lib/client/sync/stream/AbstractRemote.d.ts +0 -180
  150. package/lib/client/sync/stream/AbstractRemote.js +0 -505
  151. package/lib/client/sync/stream/AbstractRemote.js.map +0 -1
  152. package/lib/client/sync/stream/AbstractStreamingSyncImplementation.d.ts +0 -265
  153. package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js +0 -705
  154. package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js.map +0 -1
  155. package/lib/client/sync/stream/WebsocketClientTransport.d.ts +0 -15
  156. package/lib/client/sync/stream/WebsocketClientTransport.js +0 -62
  157. package/lib/client/sync/stream/WebsocketClientTransport.js.map +0 -1
  158. package/lib/client/sync/stream/core-instruction.d.ts +0 -77
  159. package/lib/client/sync/stream/core-instruction.js +0 -30
  160. package/lib/client/sync/stream/core-instruction.js.map +0 -1
  161. package/lib/client/triggers/MemoryTriggerClaimManager.d.ts +0 -6
  162. package/lib/client/triggers/MemoryTriggerClaimManager.js +0 -21
  163. package/lib/client/triggers/MemoryTriggerClaimManager.js.map +0 -1
  164. package/lib/client/triggers/TriggerManagerImpl.d.ts +0 -39
  165. package/lib/client/triggers/TriggerManagerImpl.js +0 -410
  166. package/lib/client/triggers/TriggerManagerImpl.js.map +0 -1
  167. package/lib/client/watched/processors/AbstractQueryProcessor.d.ts +0 -68
  168. package/lib/client/watched/processors/AbstractQueryProcessor.js +0 -151
  169. package/lib/client/watched/processors/AbstractQueryProcessor.js.map +0 -1
  170. package/lib/db/ConnectionClosedError.d.ts +0 -12
  171. package/lib/db/ConnectionClosedError.js +0 -23
  172. package/lib/db/ConnectionClosedError.js.map +0 -1
  173. package/lib/db/schema/TableV2.d.ts +0 -11
  174. package/lib/db/schema/TableV2.js +0 -11
  175. package/lib/db/schema/TableV2.js.map +0 -1
  176. package/lib/utils/AbortOperation.d.ts +0 -11
  177. package/lib/utils/AbortOperation.js +0 -21
  178. package/lib/utils/AbortOperation.js.map +0 -1
  179. package/lib/utils/ControlledExecutor.d.ts +0 -31
  180. package/lib/utils/ControlledExecutor.js +0 -54
  181. package/lib/utils/ControlledExecutor.js.map +0 -1
  182. package/lib/utils/async.d.ts +0 -46
  183. package/lib/utils/async.js +0 -147
  184. package/lib/utils/async.js.map +0 -1
  185. package/lib/utils/compatibility.d.ts +0 -8
  186. package/lib/utils/compatibility.js +0 -9
  187. package/lib/utils/compatibility.js.map +0 -1
  188. package/lib/utils/parseQuery.d.ts +0 -12
  189. package/lib/utils/parseQuery.js +0 -20
  190. package/lib/utils/parseQuery.js.map +0 -1
  191. package/lib/utils/queue.d.ts +0 -16
  192. package/lib/utils/queue.js +0 -42
  193. package/lib/utils/queue.js.map +0 -1
  194. package/lib/utils/stream_transform.d.ts +0 -41
  195. package/lib/utils/stream_transform.js +0 -211
  196. package/lib/utils/stream_transform.js.map +0 -1
  197. package/src/client/AbstractPowerSyncDatabase.ts +0 -1408
  198. package/src/client/AbstractPowerSyncOpenFactory.ts +0 -45
  199. package/src/client/ConnectionManager.ts +0 -405
  200. package/src/client/CustomQuery.ts +0 -56
  201. package/src/client/constants.ts +0 -4
  202. package/src/client/sync/bucket/BucketStorageAdapter.ts +0 -65
  203. package/src/client/sync/bucket/SqliteBucketStorage.ts +0 -202
  204. package/src/client/sync/stream/AbstractRemote.ts +0 -644
  205. package/src/client/sync/stream/AbstractStreamingSyncImplementation.ts +0 -970
  206. package/src/client/sync/stream/WebsocketClientTransport.ts +0 -82
  207. package/src/client/sync/stream/core-instruction.ts +0 -109
  208. package/src/client/triggers/MemoryTriggerClaimManager.ts +0 -25
  209. package/src/client/triggers/TriggerManagerImpl.ts +0 -501
  210. package/src/client/watched/processors/AbstractQueryProcessor.ts +0 -226
  211. package/src/db/ConnectionClosedError.ts +0 -25
  212. package/src/db/schema/TableV2.ts +0 -11
  213. package/src/utils/AbortOperation.ts +0 -19
  214. package/src/utils/ControlledExecutor.ts +0 -78
  215. package/src/utils/async.ts +0 -185
  216. package/src/utils/compatibility.ts +0 -9
  217. package/src/utils/parseQuery.ts +0 -31
  218. package/src/utils/queue.ts +0 -48
  219. package/src/utils/stream_transform.ts +0 -260
@@ -1,970 +0,0 @@
1
- import Logger, { ILogger } from 'js-logger';
2
-
3
- import { SyncStatus, SyncStatusOptions } from '../../../db/crud/SyncStatus.js';
4
- import { AbortOperation } from '../../../utils/AbortOperation.js';
5
- import { BaseListener, BaseObserver, BaseObserverInterface, Disposable } from '../../../utils/BaseObserver.js';
6
- import { BucketStorageAdapter, PowerSyncControlCommand } from '../bucket/BucketStorageAdapter.js';
7
- import { CrudEntry } from '../bucket/CrudEntry.js';
8
- import { AbstractRemote, FetchStrategy, SyncStreamOptions } from './AbstractRemote.js';
9
- import {
10
- Instruction,
11
- NonInterruptingInstruction,
12
- coreStatusToJs,
13
- isInterruptingInstruction
14
- } from './core-instruction.js';
15
- import {
16
- doneResult,
17
- injectable,
18
- InjectableIterator,
19
- SimpleAsyncIterator,
20
- valueResult
21
- } from '../../../utils/stream_transform.js';
22
- import { asyncNotifier } from '../../../utils/async.js';
23
- import { StreamingSyncRequestParameterType } from './JsonValue.js';
24
-
25
- /**
26
- * @internal
27
- */
28
- export enum LockType {
29
- CRUD = 'crud',
30
- SYNC = 'sync'
31
- }
32
-
33
- /**
34
- * @public
35
- */
36
- export enum SyncStreamConnectionMethod {
37
- HTTP = 'http',
38
- WEB_SOCKET = 'web-socket'
39
- }
40
-
41
- /**
42
- * @deprecated Deprecated since {@link SyncClientImplementation.RUST} is the only option.
43
- * @public
44
- */
45
- export enum SyncClientImplementation {
46
- /**
47
- * This implementation offloads the sync line decoding and handling into the PowerSync
48
- * core extension.
49
- *
50
- * This is the only option, as an older JavaScript client implementation has been removed from the SDK.
51
- *
52
- * ## Compatibility warning
53
- *
54
- * The Rust sync client stores sync data in a format that is slightly different than the one used
55
- * by the old JavaScript client. When adopting the {@link SyncClientImplementation.RUST} client on existing databases,
56
- * the PowerSync SDK will migrate the format automatically.
57
- *
58
- * SDK versions supporting both the JavaScript and the Rust client support both formats with the JavaScript client
59
- * implementaiton. However, downgrading to an SDK version that only supports the JavaScript client would not be
60
- * possible anymore. Problematic SDK versions have been released before 2025-06-09.
61
- */
62
- RUST = 'rust'
63
- }
64
-
65
- /**
66
- * The default {@link SyncClientImplementation} to use, {@link SyncClientImplementation.RUST}.
67
- *
68
- * @deprecated Deprecated since {@link SyncClientImplementation.RUST} is the only option.
69
- * @public
70
- */
71
- export const DEFAULT_SYNC_CLIENT_IMPLEMENTATION = SyncClientImplementation.RUST;
72
-
73
- /**
74
- * Abstract Lock to be implemented by various JS environments
75
- *
76
- * @internal
77
- */
78
- export interface LockOptions<T> {
79
- callback: () => Promise<T>;
80
- type: LockType;
81
- signal?: AbortSignal;
82
- }
83
-
84
- /**
85
- * @internal
86
- */
87
- export interface AbstractStreamingSyncImplementationOptions extends RequiredAdditionalConnectionOptions {
88
- adapter: BucketStorageAdapter;
89
- subscriptions: SubscribedStream[];
90
- uploadCrud: () => Promise<void>;
91
- /**
92
- * An identifier for which PowerSync DB this sync implementation is
93
- * linked to. Most commonly DB name, but not restricted to DB name.
94
- */
95
- identifier?: string;
96
- logger?: ILogger;
97
- remote: AbstractRemote;
98
- }
99
-
100
- /**
101
- * @internal
102
- */
103
- export interface StreamingSyncImplementationListener extends BaseListener {
104
- /**
105
- * Triggered whenever a status update has been attempted to be made or
106
- * refreshed.
107
- */
108
- statusUpdated?: ((statusUpdate: SyncStatusOptions) => void) | undefined;
109
- /**
110
- * Triggers whenever the status' members have changed in value
111
- */
112
- statusChanged?: ((status: SyncStatus) => void) | undefined;
113
- }
114
-
115
- /**
116
- * Configurable options to be used when connecting to the PowerSync
117
- * backend instance.
118
- *
119
- * @public
120
- */
121
- export type PowerSyncConnectionOptions = Omit<InternalConnectionOptions, 'serializedSchema'>;
122
-
123
- /**
124
- * @internal
125
- */
126
- export interface InternalConnectionOptions extends BaseConnectionOptions, AdditionalConnectionOptions {}
127
-
128
- /** @public */
129
- export interface BaseConnectionOptions {
130
- /**
131
- * A set of metadata to be included in service logs.
132
- */
133
- appMetadata?: Record<string, string>;
134
-
135
- /**
136
- * @deprecated The Rust sync client is used unconditionally, so this option can't be configured.
137
- */
138
- clientImplementation?: SyncClientImplementation;
139
-
140
- /**
141
- * The connection method to use when streaming updates from
142
- * the PowerSync backend instance.
143
- * Defaults to a HTTP streaming connection.
144
- */
145
- connectionMethod?: SyncStreamConnectionMethod;
146
-
147
- /**
148
- * The fetch strategy to use when streaming updates from the PowerSync backend instance.
149
- */
150
- fetchStrategy?: FetchStrategy;
151
-
152
- /**
153
- * These parameters are passed to the sync rules, and will be available under the`user_parameters` object.
154
- */
155
- params?: Record<string, StreamingSyncRequestParameterType>;
156
-
157
- /**
158
- * Whether to include streams that have `auto_subscribe: true` in their definition.
159
- *
160
- * This defaults to `true`.
161
- */
162
- includeDefaultStreams?: boolean;
163
-
164
- /**
165
- * The serialized schema - mainly used to forward information about raw tables to the sync client.
166
- */
167
- serializedSchema?: any;
168
- }
169
-
170
- /** @internal */
171
- export interface AdditionalConnectionOptions {
172
- /**
173
- * Delay for retrying sync streaming operations
174
- * from the PowerSync backend after an error occurs.
175
- */
176
- retryDelayMs?: number;
177
- /**
178
- * Backend Connector CRUD operations are throttled
179
- * to occur at most every `crudUploadThrottleMs`
180
- * milliseconds.
181
- */
182
- crudUploadThrottleMs?: number;
183
- }
184
-
185
- /** @internal */
186
- export interface RequiredAdditionalConnectionOptions extends Required<AdditionalConnectionOptions> {
187
- subscriptions: SubscribedStream[];
188
- }
189
-
190
- /**
191
- * @internal
192
- */
193
- export interface StreamingSyncImplementation
194
- extends BaseObserverInterface<StreamingSyncImplementationListener>, Disposable {
195
- /**
196
- * Connects to the sync service
197
- */
198
- connect(options?: InternalConnectionOptions): Promise<void>;
199
- /**
200
- * Disconnects from the sync services.
201
- * @throws if not connected or if abort is not controlled internally
202
- */
203
- disconnect(): Promise<void>;
204
- getWriteCheckpoint: () => Promise<string>;
205
- isConnected: boolean;
206
- syncStatus: SyncStatus;
207
- triggerCrudUpload: () => void;
208
- waitForReady(): Promise<void>;
209
- waitForStatus(status: SyncStatusOptions): Promise<void>;
210
- waitUntilStatusMatches(predicate: (status: SyncStatus) => boolean): Promise<void>;
211
- updateSubscriptions(subscriptions: SubscribedStream[]): void;
212
- markConnectionMayHaveChanged(): void;
213
- }
214
-
215
- /**
216
- * @internal
217
- */
218
- export const DEFAULT_CRUD_UPLOAD_THROTTLE_MS = 1000;
219
- /**
220
- * @internal
221
- */
222
- export const DEFAULT_RETRY_DELAY_MS = 5000;
223
-
224
- /**
225
- * @internal
226
- */
227
- export const DEFAULT_STREAMING_SYNC_OPTIONS = {
228
- retryDelayMs: DEFAULT_RETRY_DELAY_MS,
229
- crudUploadThrottleMs: DEFAULT_CRUD_UPLOAD_THROTTLE_MS
230
- };
231
-
232
- /**
233
- * @internal
234
- */
235
- export type RequiredPowerSyncConnectionOptions = Required<BaseConnectionOptions>;
236
-
237
- /**
238
- * @internal
239
- */
240
- export const DEFAULT_STREAM_CONNECTION_OPTIONS: RequiredPowerSyncConnectionOptions = {
241
- appMetadata: {},
242
- connectionMethod: SyncStreamConnectionMethod.WEB_SOCKET,
243
- clientImplementation: DEFAULT_SYNC_CLIENT_IMPLEMENTATION,
244
- fetchStrategy: FetchStrategy.Buffered,
245
- params: {},
246
- serializedSchema: undefined,
247
- includeDefaultStreams: true
248
- };
249
-
250
- /**
251
- * @internal
252
- */
253
- export type SubscribedStream = {
254
- name: string;
255
- params: Record<string, any> | null;
256
- };
257
-
258
- /**
259
- * @internal
260
- */
261
- export abstract class AbstractStreamingSyncImplementation
262
- extends BaseObserver<StreamingSyncImplementationListener>
263
- implements StreamingSyncImplementation
264
- {
265
- protected options: AbstractStreamingSyncImplementationOptions;
266
- protected abortController: AbortController | null;
267
- protected crudUpdateListener?: () => void;
268
- protected streamingSyncPromise?: Promise<[void, void]>;
269
- protected logger: ILogger;
270
- private activeStreams: SubscribedStream[];
271
- private connectionMayHaveChanged = false;
272
- private crudUploadNotifier = asyncNotifier();
273
-
274
- private notifyCompletedUploads?: () => void;
275
- private handleActiveStreamsChange?: () => void;
276
-
277
- syncStatus: SyncStatus;
278
-
279
- constructor(options: AbstractStreamingSyncImplementationOptions) {
280
- super();
281
- this.options = options;
282
- this.activeStreams = options.subscriptions;
283
- this.logger = options.logger ?? Logger.get('PowerSyncStream');
284
-
285
- this.syncStatus = new SyncStatus({
286
- connected: false,
287
- connecting: false,
288
- lastSyncedAt: undefined,
289
- dataFlow: {
290
- uploading: false,
291
- downloading: false
292
- }
293
- });
294
- this.abortController = null;
295
- }
296
-
297
- triggerCrudUpload() {
298
- this.crudUploadNotifier.notify();
299
- }
300
-
301
- async waitForReady() {}
302
-
303
- waitForStatus(status: SyncStatusOptions): Promise<void> {
304
- return this.waitUntilStatusMatches((currentStatus) => {
305
- /**
306
- * Match only the partial status options provided in the
307
- * matching status
308
- */
309
- const matchPartialObject = (compA: object, compB: any): any => {
310
- return Object.entries(compA).every(([key, value]) => {
311
- const comparisonBValue = compB[key];
312
- if (typeof value == 'object' && typeof comparisonBValue == 'object') {
313
- return matchPartialObject(value, comparisonBValue);
314
- }
315
- return value == comparisonBValue;
316
- });
317
- };
318
-
319
- return matchPartialObject(status, currentStatus);
320
- });
321
- }
322
-
323
- waitUntilStatusMatches(predicate: (status: SyncStatus) => boolean): Promise<void> {
324
- return new Promise((resolve) => {
325
- if (predicate(this.syncStatus)) {
326
- resolve();
327
- return;
328
- }
329
-
330
- const l = this.registerListener({
331
- statusChanged: (updatedStatus) => {
332
- if (predicate(updatedStatus)) {
333
- resolve();
334
- l?.();
335
- }
336
- }
337
- });
338
- });
339
- }
340
-
341
- get lastSyncedAt() {
342
- const lastSynced = this.syncStatus.lastSyncedAt;
343
- return lastSynced && new Date(lastSynced);
344
- }
345
-
346
- get isConnected() {
347
- return this.syncStatus.connected;
348
- }
349
-
350
- async dispose() {
351
- super.dispose();
352
- this.crudUpdateListener?.();
353
- this.crudUpdateListener = undefined;
354
- }
355
-
356
- abstract obtainLock<T>(lockOptions: LockOptions<T>): Promise<T>;
357
-
358
- async getWriteCheckpoint(): Promise<string> {
359
- const clientId = await this.options.adapter.getClientId();
360
- let path = `/write-checkpoint2.json?client_id=${clientId}`;
361
- const response = await this.options.remote.get(path);
362
- const checkpoint = response['data']['write_checkpoint'] as string;
363
- this.logger.debug(`Created write checkpoint: ${checkpoint}`);
364
- return checkpoint;
365
- }
366
-
367
- private async crudUploadLoop(signal: AbortSignal): Promise<void> {
368
- while (!signal.aborted) {
369
- await Promise.all([
370
- // Start the initial CRUD upload on connect. Then, keep polling until we're done.
371
- this._uploadAllCrud(signal),
372
- this.delayRetry(signal, this.options.crudUploadThrottleMs!)
373
- ]);
374
-
375
- await this.crudUploadNotifier.waitForNotification(signal);
376
- }
377
- }
378
-
379
- private async _uploadAllCrud(signal: AbortSignal): Promise<void> {
380
- return this.obtainLock({
381
- type: LockType.CRUD,
382
- callback: async () => {
383
- /**
384
- * Keep track of the first item in the CRUD queue for the last `uploadCrud` iteration.
385
- */
386
- let checkedCrudItem: CrudEntry | undefined;
387
-
388
- while (!signal.aborted) {
389
- try {
390
- /**
391
- * This is the first item in the FIFO CRUD queue.
392
- */
393
- const nextCrudItem = await this.options.adapter.nextCrudItem();
394
- if (nextCrudItem) {
395
- this.updateSyncStatus({
396
- dataFlow: {
397
- uploading: true
398
- }
399
- });
400
-
401
- if (nextCrudItem.clientId == checkedCrudItem?.clientId) {
402
- // This will force a higher log level than exceptions which are caught here.
403
- this.logger.warn(`Potentially previously uploaded CRUD entries are still present in the upload queue.
404
- Make sure to handle uploads and complete CRUD transactions or batches by calling and awaiting their [.complete()] method.
405
- The next upload iteration will be delayed.`);
406
- throw new Error('Delaying due to previously encountered CRUD item.');
407
- }
408
-
409
- checkedCrudItem = nextCrudItem;
410
- await this.options.uploadCrud();
411
- this.updateSyncStatus({
412
- dataFlow: {
413
- uploadError: undefined
414
- }
415
- });
416
- } else {
417
- // Uploading is completed
418
- const neededUpdate = await this.options.adapter.updateLocalTarget(() => this.getWriteCheckpoint());
419
- if (neededUpdate) {
420
- this.notifyCompletedUploads?.();
421
- } else if (checkedCrudItem != null) {
422
- // Only log this if there was something to upload
423
- this.logger.debug('Upload complete, no write checkpoint needed.');
424
- }
425
- break;
426
- }
427
- } catch (ex) {
428
- checkedCrudItem = undefined;
429
- this.updateSyncStatus({
430
- dataFlow: {
431
- uploading: false,
432
- uploadError: ex as Error
433
- }
434
- });
435
- await this.delayRetry(signal);
436
- if (!this.isConnected) {
437
- // Exit the upload loop if the sync stream is no longer connected
438
- break;
439
- }
440
- this.logger.debug(
441
- `Caught exception when uploading. Upload will retry after a delay. Exception: ${(ex as Error).message}`
442
- );
443
- } finally {
444
- this.updateSyncStatus({
445
- dataFlow: {
446
- uploading: false
447
- }
448
- });
449
- }
450
- }
451
- }
452
- });
453
- }
454
-
455
- async connect(options?: PowerSyncConnectionOptions) {
456
- if (this.abortController) {
457
- await this.disconnect();
458
- }
459
-
460
- const controller = new AbortController();
461
- this.abortController = controller;
462
- this.streamingSyncPromise = Promise.all([
463
- this.crudUploadLoop(controller.signal).catch((ex) => this.logger.error('Error in crud upload loop', ex)),
464
- this.streamingSync(controller.signal, options)
465
- ]);
466
-
467
- // Return a promise that resolves when the connection status is updated to indicate that we're connected.
468
- return new Promise<void>((resolve) => {
469
- const disposer = this.registerListener({
470
- statusChanged: (status) => {
471
- if (status.dataFlowStatus.downloadError != null) {
472
- this.logger.warn('Initial connect attempt did not successfully connect to server');
473
- } else if (status.connecting) {
474
- // Still connecting.
475
- return;
476
- }
477
-
478
- disposer();
479
- resolve();
480
- }
481
- });
482
- });
483
- }
484
-
485
- async disconnect(): Promise<void> {
486
- if (!this.abortController) {
487
- return;
488
- }
489
-
490
- // This might be called multiple times
491
- if (!this.abortController.signal.aborted) {
492
- this.abortController.abort(new AbortOperation('Disconnect has been requested'));
493
- }
494
-
495
- // Await any pending operations before completing the disconnect operation
496
- try {
497
- await this.streamingSyncPromise;
498
- } catch (ex) {
499
- // The operation might have failed, all we care about is if it has completed
500
- this.logger.warn(ex);
501
- }
502
- this.streamingSyncPromise = undefined;
503
-
504
- this.abortController = null;
505
- this.updateSyncStatus({ connected: false, connecting: false });
506
- }
507
-
508
- private async streamingSync(signal: AbortSignal, options?: PowerSyncConnectionOptions): Promise<void> {
509
- /**
510
- * Listen for CRUD updates and trigger upstream uploads
511
- */
512
- this.crudUpdateListener = this.options.adapter.registerListener({
513
- crudUpdate: () => this.triggerCrudUpload()
514
- });
515
-
516
- /**
517
- * Create a new abort controller which aborts items downstream.
518
- * This is needed to close any previous connections on exception.
519
- */
520
- let nestedAbortController = new AbortController();
521
-
522
- signal.addEventListener('abort', () => {
523
- /**
524
- * A request for disconnect was received upstream. Relay the request
525
- * to the nested abort controller.
526
- */
527
- nestedAbortController.abort(signal?.reason ?? new AbortOperation('Received command to disconnect from upstream'));
528
- this.crudUpdateListener?.();
529
- this.crudUpdateListener = undefined;
530
- this.updateSyncStatus({
531
- connected: false,
532
- connecting: false,
533
- dataFlow: {
534
- downloading: false,
535
- downloadProgress: null
536
- }
537
- });
538
- });
539
-
540
- /**
541
- * This loops runs until [retry] is false or the abort signal is set to aborted.
542
- * Aborting the nestedAbortController will:
543
- * - Abort any pending fetch requests
544
- * - Close any sync stream ReadableStreams (which will also close any established network requests)
545
- */
546
- while (true) {
547
- this.updateSyncStatus({ connecting: true });
548
- let shouldDelayRetry = true;
549
- let result: RustIterationResult | null = null;
550
-
551
- try {
552
- if (signal?.aborted) {
553
- break;
554
- }
555
- result = await this.streamingSyncIteration(nestedAbortController.signal, options);
556
- // Continue immediately, streamingSyncIteration will wait before completing if necessary.
557
- } catch (ex) {
558
- /**
559
- * Either:
560
- * - A network request failed with a failed connection or not OKAY response code.
561
- * - There was a sync processing error.
562
- * - The connection was aborted.
563
- * This loop will retry after a delay if the connection was not aborted.
564
- * The nested abort controller will cleanup any open network requests and streams.
565
- * The WebRemote should only abort pending fetch requests or close active Readable streams.
566
- */
567
-
568
- if (ex instanceof AbortOperation) {
569
- this.logger.warn(ex);
570
- shouldDelayRetry = false;
571
- // A disconnect was requested, we should not delay since there is no explicit retry
572
- } else if (this.connectionMayHaveChanged && (ex as Error).message?.indexOf('No iteration is active') >= 0) {
573
- this.connectionMayHaveChanged = false;
574
- this.logger.info('Sync error after changed connection, retrying immediately');
575
- shouldDelayRetry = false;
576
- } else {
577
- this.logger.error(ex);
578
- }
579
-
580
- this.updateSyncStatus({
581
- dataFlow: {
582
- downloadError: ex as Error
583
- }
584
- });
585
- } finally {
586
- this.notifyCompletedUploads = undefined;
587
-
588
- if (!signal.aborted) {
589
- nestedAbortController.abort(new AbortOperation('Closing sync stream network requests before retry.'));
590
- nestedAbortController = new AbortController();
591
- }
592
-
593
- if (result?.immediateRestart != true) {
594
- this.updateSyncStatus({
595
- connected: false,
596
- connecting: true // May be unnecessary
597
- });
598
-
599
- // On error, wait a little before retrying
600
- if (shouldDelayRetry) {
601
- await this.delayRetry(nestedAbortController.signal);
602
- }
603
- }
604
- }
605
- }
606
-
607
- // Mark as disconnected if here
608
- this.updateSyncStatus({ connected: false, connecting: false });
609
- }
610
-
611
- markConnectionMayHaveChanged() {
612
- // By setting this field, we'll immediately retry if the next sync event causes an error triggered by us not having
613
- // an active sync iteration on the connection in use.
614
- this.connectionMayHaveChanged = true;
615
-
616
- // This triggers a `powersync_control` invocation if a sync iteration is currently active. This is a cheap call to
617
- // make when no subscriptions have actually changed, we're mainly interested in this immediately throwing if no
618
- // iteration is active. That allows us to reconnect ASAP, instead of having to wait for the next sync line.
619
- this.handleActiveStreamsChange?.();
620
- }
621
-
622
- /**
623
- * Older versions of the JS SDK used to encode subkeys as JSON in `OplogEntry.toJSON`.
624
- * Because subkeys are always strings, this leads to quotes being added around them in `ps_oplog`.
625
- * While this is not a problem as long as it's done consistently, it causes issues when a database
626
- * created by the JS SDK is used with other SDKs, or (more likely) when the new Rust sync client
627
- * is enabled.
628
- *
629
- * So, we add a migration from the old key format (with quotes) to the new one (no quotes). The
630
- * migration is only triggered when necessary (for now). The function returns whether the new format
631
- * should be used, so that the JS SDK is able to write to updated databases.
632
- *
633
- * @param requireFixedKeyFormat - Whether we require the new format or also support the old one.
634
- * The Rust client requires the new subkey format.
635
- * @returns Whether the database is now using the new, fixed subkey format.
636
- */
637
- private async requireKeyFormat(requireFixedKeyFormat: boolean): Promise<boolean> {
638
- const hasMigrated = await this.options.adapter.hasMigratedSubkeys();
639
- if (requireFixedKeyFormat && !hasMigrated) {
640
- await this.options.adapter.migrateToFixedSubkeys();
641
- return true;
642
- } else {
643
- return hasMigrated;
644
- }
645
- }
646
-
647
- protected streamingSyncIteration(
648
- signal: AbortSignal,
649
- options?: PowerSyncConnectionOptions
650
- ): Promise<RustIterationResult | null> {
651
- return this.obtainLock({
652
- type: LockType.SYNC,
653
- signal,
654
- callback: async () => {
655
- const resolvedOptions: RequiredPowerSyncConnectionOptions = {
656
- ...DEFAULT_STREAM_CONNECTION_OPTIONS,
657
- ...(options ?? {})
658
- };
659
-
660
- // Validate app metadata
661
- const invalidMetadata = Object.entries(resolvedOptions.appMetadata).filter(
662
- ([_, value]) => typeof value != 'string'
663
- );
664
- if (invalidMetadata.length > 0) {
665
- throw new Error(
666
- `Invalid appMetadata provided. Only string values are allowed. Invalid values: ${invalidMetadata.map(([key, value]) => `${key}: ${value}`).join(', ')}`
667
- );
668
- }
669
-
670
- await this.requireKeyFormat(true);
671
- return await this.rustSyncIteration(signal, resolvedOptions);
672
- }
673
- });
674
- }
675
-
676
- private receiveSyncLines(data: {
677
- options: SyncStreamOptions;
678
- connection: RequiredPowerSyncConnectionOptions;
679
- }): SimpleAsyncIterator<EnqueuedCommand> {
680
- const { options, connection } = data;
681
- const remote = this.options.remote;
682
-
683
- const openInner = async () => {
684
- if (connection.connectionMethod == SyncStreamConnectionMethod.HTTP) {
685
- return await remote.fetchStream(options);
686
- } else {
687
- return await this.options.remote.socketStreamRaw({
688
- ...options,
689
- ...{ fetchStrategy: connection.fetchStrategy }
690
- });
691
- }
692
- };
693
-
694
- let inner: SimpleAsyncIterator<string | Uint8Array> | undefined;
695
- let done = false;
696
-
697
- return {
698
- async next(): Promise<IteratorResult<EnqueuedCommand>> {
699
- if (done) {
700
- return doneResult;
701
- } else if (inner == null) {
702
- inner = await openInner();
703
- // We're connected here, so we can tell the core extension about it.
704
- return valueResult<EnqueuedCommand>({
705
- command: PowerSyncControlCommand.CONNECTION_STATE,
706
- payload: 'established'
707
- });
708
- } else {
709
- const event = await inner.next();
710
- if (event.done) {
711
- done = true;
712
- return valueResult<EnqueuedCommand>({ command: PowerSyncControlCommand.CONNECTION_STATE, payload: 'end' });
713
- } else {
714
- return valueResult<EnqueuedCommand>({
715
- command:
716
- typeof event.value == 'string'
717
- ? PowerSyncControlCommand.PROCESS_TEXT_LINE
718
- : PowerSyncControlCommand.PROCESS_BSON_LINE,
719
- payload: event.value
720
- });
721
- }
722
- }
723
- }
724
- };
725
- }
726
-
727
- private async rustSyncIteration(
728
- signal: AbortSignal,
729
- resolvedOptions: RequiredPowerSyncConnectionOptions
730
- ): Promise<RustIterationResult> {
731
- const syncImplementation = this;
732
- const adapter = this.options.adapter;
733
- const remote = this.options.remote;
734
- let hideDisconnectOnRestart = false;
735
- let notifyTokenRefreshed: (() => void) | undefined;
736
-
737
- if (signal.aborted) {
738
- throw new AbortOperation('Connection request has been aborted');
739
- }
740
-
741
- function startCommand() {
742
- const options: any = {
743
- parameters: resolvedOptions.params,
744
- app_metadata: resolvedOptions.appMetadata,
745
- active_streams: syncImplementation.activeStreams,
746
- include_defaults: resolvedOptions.includeDefaultStreams
747
- };
748
- if (resolvedOptions.serializedSchema) {
749
- options.schema = resolvedOptions.serializedSchema;
750
- }
751
-
752
- return invokePowerSyncControl(PowerSyncControlCommand.START, JSON.stringify(options));
753
- }
754
-
755
- async function stop() {
756
- const instructions = await invokePowerSyncControl(PowerSyncControlCommand.STOP);
757
- for (const instruction of instructions) {
758
- // We don't need to handle interrupting instructions since we're unconditionally ending the sync iteration at
759
- // this point.
760
- if (isInterruptingInstruction(instruction)) continue;
761
- await handleInstruction(instruction);
762
- }
763
- }
764
-
765
- async function invokePowerSyncControl(
766
- op: PowerSyncControlCommand,
767
- payload?: Uint8Array | string
768
- ): Promise<Instruction[]> {
769
- const rawResponse = await adapter.control(op, payload ?? null);
770
- const logger = syncImplementation.logger;
771
- logger.trace(
772
- 'powersync_control',
773
- op,
774
- payload == null || typeof payload == 'string' ? payload : '<bytes>',
775
- rawResponse
776
- );
777
-
778
- if (op != PowerSyncControlCommand.STOP) {
779
- // Evidently we have a working connection here, otherwise powersync_control would have failed.
780
- syncImplementation.connectionMayHaveChanged = false;
781
- }
782
-
783
- return JSON.parse(rawResponse);
784
- }
785
-
786
- async function handleInstruction(instruction: NonInterruptingInstruction) {
787
- if ('LogLine' in instruction) {
788
- switch (instruction.LogLine.severity) {
789
- case 'DEBUG':
790
- syncImplementation.logger.debug(instruction.LogLine.line);
791
- break;
792
- case 'INFO':
793
- syncImplementation.logger.info(instruction.LogLine.line);
794
- break;
795
- case 'WARNING':
796
- syncImplementation.logger.warn(instruction.LogLine.line);
797
- break;
798
- }
799
- } else if ('UpdateSyncStatus' in instruction) {
800
- syncImplementation.updateSyncStatus(coreStatusToJs(instruction.UpdateSyncStatus.status));
801
- } else if ('FetchCredentials' in instruction) {
802
- if (instruction.FetchCredentials.did_expire) {
803
- remote.invalidateCredentials();
804
- } else {
805
- remote.invalidateCredentials();
806
-
807
- // Restart iteration after the credentials have been refreshed.
808
- remote.fetchCredentials().then(
809
- (_) => {
810
- notifyTokenRefreshed?.();
811
- },
812
- (err) => {
813
- syncImplementation.logger.warn('Could not prefetch credentials', err);
814
- }
815
- );
816
- }
817
- } else if ('FlushFileSystem' in instruction) {
818
- // Not necessary on JS platforms.
819
- } else if ('DidCompleteSync' in instruction) {
820
- syncImplementation.updateSyncStatus({
821
- dataFlow: {
822
- downloadError: undefined
823
- }
824
- });
825
- }
826
- }
827
-
828
- try {
829
- const defaultResult = { immediateRestart: false };
830
- // Pending sync lines received from the service, as well as local events that trigger a powersync_control
831
- // invocation (local events include refreshed tokens and completed uploads).
832
- // This is a single data stream so that we can handle all control calls from a single place.
833
- let controlInvocations: InjectableIterator<EnqueuedCommand> | null = null;
834
-
835
- for (const startInstruction of await startCommand()) {
836
- if ('EstablishSyncStream' in startInstruction) {
837
- const syncOptions: SyncStreamOptions = {
838
- path: '/sync/stream',
839
- abortSignal: signal,
840
- data: startInstruction.EstablishSyncStream.request
841
- };
842
-
843
- controlInvocations = injectable(
844
- syncImplementation.receiveSyncLines({
845
- options: syncOptions,
846
- connection: resolvedOptions
847
- })
848
- );
849
- } else if ('CloseSyncStream' in startInstruction) {
850
- return defaultResult;
851
- } else {
852
- await handleInstruction(startInstruction);
853
- }
854
- }
855
- if (controlInvocations == null) return defaultResult;
856
-
857
- this.notifyCompletedUploads = () => {
858
- controlInvocations.inject({ command: PowerSyncControlCommand.NOTIFY_CRUD_UPLOAD_COMPLETED });
859
- };
860
- this.handleActiveStreamsChange = () => {
861
- controlInvocations.inject({
862
- command: PowerSyncControlCommand.UPDATE_SUBSCRIPTIONS,
863
- payload: JSON.stringify(this.activeStreams)
864
- });
865
- };
866
- notifyTokenRefreshed = () => {
867
- controlInvocations.inject({
868
- command: PowerSyncControlCommand.NOTIFY_TOKEN_REFRESHED
869
- });
870
- };
871
-
872
- let hadSyncLine = false;
873
- loop: while (true) {
874
- const { done, value } = await controlInvocations.next();
875
- if (done) break;
876
-
877
- if (!hadSyncLine) {
878
- // Trigger a local CRUD upload when the first sync line has been received, this allows uploading local changes
879
- // that have been made while offline or disconnected.
880
- if (
881
- value.command == PowerSyncControlCommand.PROCESS_TEXT_LINE ||
882
- value.command == PowerSyncControlCommand.PROCESS_BSON_LINE
883
- ) {
884
- hadSyncLine = true;
885
- this.triggerCrudUpload?.();
886
- }
887
- }
888
-
889
- const instructions = await invokePowerSyncControl(value.command, value.payload);
890
- for (const instruction of instructions) {
891
- if ('EstablishSyncStream' in instruction) {
892
- throw new Error('Received EstablishSyncStream while already connected.');
893
- } else if ('CloseSyncStream' in instruction) {
894
- hideDisconnectOnRestart = instruction.CloseSyncStream.hide_disconnect;
895
- break loop;
896
- } else {
897
- await handleInstruction(instruction);
898
- }
899
- }
900
- }
901
- } finally {
902
- this.notifyCompletedUploads = this.handleActiveStreamsChange = undefined;
903
- notifyTokenRefreshed = undefined;
904
- await stop();
905
- }
906
-
907
- return { immediateRestart: hideDisconnectOnRestart };
908
- }
909
-
910
- protected updateSyncStatus(options: SyncStatusOptions) {
911
- const updatedStatus = new SyncStatus({
912
- connected: options.connected ?? this.syncStatus.connected,
913
- connecting: !options.connected && (options.connecting ?? this.syncStatus.connecting),
914
- lastSyncedAt: options.lastSyncedAt ?? this.syncStatus.lastSyncedAt,
915
- dataFlow: {
916
- ...this.syncStatus.dataFlowStatus,
917
- ...options.dataFlow
918
- },
919
- priorityStatusEntries: options.priorityStatusEntries ?? this.syncStatus.priorityStatusEntries,
920
- clientImplementation: options.clientImplementation ?? this.syncStatus.clientImplementation
921
- });
922
-
923
- if (!this.syncStatus.isEqual(updatedStatus)) {
924
- this.syncStatus = updatedStatus;
925
- // Only trigger this is there was a change
926
- this.iterateListeners((cb) => cb.statusChanged?.(updatedStatus));
927
- }
928
-
929
- // trigger this for all updates
930
- this.iterateListeners((cb) => cb.statusUpdated?.(options));
931
- }
932
-
933
- private async delayRetry(signal?: AbortSignal, delay = this.options.retryDelayMs): Promise<void> {
934
- return new Promise((resolve) => {
935
- if (signal?.aborted) {
936
- // If the signal is already aborted, resolve immediately
937
- resolve();
938
- return;
939
- }
940
-
941
- let timeoutId: ReturnType<typeof setTimeout> | undefined;
942
-
943
- const endDelay = () => {
944
- resolve();
945
- if (timeoutId) {
946
- clearTimeout(timeoutId);
947
- timeoutId = undefined;
948
- }
949
- signal?.removeEventListener('abort', endDelay);
950
- };
951
-
952
- signal?.addEventListener('abort', endDelay, { once: true });
953
- timeoutId = setTimeout(endDelay, delay);
954
- });
955
- }
956
-
957
- updateSubscriptions(subscriptions: SubscribedStream[]): void {
958
- this.activeStreams = subscriptions;
959
- this.handleActiveStreamsChange?.();
960
- }
961
- }
962
-
963
- interface EnqueuedCommand {
964
- command: PowerSyncControlCommand;
965
- payload?: Uint8Array | string;
966
- }
967
-
968
- interface RustIterationResult {
969
- immediateRestart: boolean;
970
- }