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