@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,705 +0,0 @@
1
- import Logger from 'js-logger';
2
- import { SyncStatus } from '../../../db/crud/SyncStatus.js';
3
- import { AbortOperation } from '../../../utils/AbortOperation.js';
4
- import { BaseObserver } from '../../../utils/BaseObserver.js';
5
- import { PowerSyncControlCommand } from '../bucket/BucketStorageAdapter.js';
6
- import { FetchStrategy } from './AbstractRemote.js';
7
- import { coreStatusToJs, isInterruptingInstruction } from './core-instruction.js';
8
- import { doneResult, injectable, valueResult } from '../../../utils/stream_transform.js';
9
- import { asyncNotifier } from '../../../utils/async.js';
10
- /**
11
- * @internal
12
- */
13
- export var LockType;
14
- (function (LockType) {
15
- LockType["CRUD"] = "crud";
16
- LockType["SYNC"] = "sync";
17
- })(LockType || (LockType = {}));
18
- /**
19
- * @public
20
- */
21
- export var SyncStreamConnectionMethod;
22
- (function (SyncStreamConnectionMethod) {
23
- SyncStreamConnectionMethod["HTTP"] = "http";
24
- SyncStreamConnectionMethod["WEB_SOCKET"] = "web-socket";
25
- })(SyncStreamConnectionMethod || (SyncStreamConnectionMethod = {}));
26
- /**
27
- * @deprecated Deprecated since {@link SyncClientImplementation.RUST} is the only option.
28
- * @public
29
- */
30
- export var SyncClientImplementation;
31
- (function (SyncClientImplementation) {
32
- /**
33
- * This implementation offloads the sync line decoding and handling into the PowerSync
34
- * core extension.
35
- *
36
- * This is the only option, as an older JavaScript client implementation has been removed from the SDK.
37
- *
38
- * ## Compatibility warning
39
- *
40
- * The Rust sync client stores sync data in a format that is slightly different than the one used
41
- * by the old JavaScript client. When adopting the {@link SyncClientImplementation.RUST} client on existing databases,
42
- * the PowerSync SDK will migrate the format automatically.
43
- *
44
- * SDK versions supporting both the JavaScript and the Rust client support both formats with the JavaScript client
45
- * implementaiton. However, downgrading to an SDK version that only supports the JavaScript client would not be
46
- * possible anymore. Problematic SDK versions have been released before 2025-06-09.
47
- */
48
- SyncClientImplementation["RUST"] = "rust";
49
- })(SyncClientImplementation || (SyncClientImplementation = {}));
50
- /**
51
- * The default {@link SyncClientImplementation} to use, {@link SyncClientImplementation.RUST}.
52
- *
53
- * @deprecated Deprecated since {@link SyncClientImplementation.RUST} is the only option.
54
- * @public
55
- */
56
- export const DEFAULT_SYNC_CLIENT_IMPLEMENTATION = SyncClientImplementation.RUST;
57
- /**
58
- * @internal
59
- */
60
- export const DEFAULT_CRUD_UPLOAD_THROTTLE_MS = 1000;
61
- /**
62
- * @internal
63
- */
64
- export const DEFAULT_RETRY_DELAY_MS = 5000;
65
- /**
66
- * @internal
67
- */
68
- export const DEFAULT_STREAMING_SYNC_OPTIONS = {
69
- retryDelayMs: DEFAULT_RETRY_DELAY_MS,
70
- crudUploadThrottleMs: DEFAULT_CRUD_UPLOAD_THROTTLE_MS
71
- };
72
- /**
73
- * @internal
74
- */
75
- export const DEFAULT_STREAM_CONNECTION_OPTIONS = {
76
- appMetadata: {},
77
- connectionMethod: SyncStreamConnectionMethod.WEB_SOCKET,
78
- clientImplementation: DEFAULT_SYNC_CLIENT_IMPLEMENTATION,
79
- fetchStrategy: FetchStrategy.Buffered,
80
- params: {},
81
- serializedSchema: undefined,
82
- includeDefaultStreams: true
83
- };
84
- /**
85
- * @internal
86
- */
87
- export class AbstractStreamingSyncImplementation extends BaseObserver {
88
- options;
89
- abortController;
90
- crudUpdateListener;
91
- streamingSyncPromise;
92
- logger;
93
- activeStreams;
94
- connectionMayHaveChanged = false;
95
- crudUploadNotifier = asyncNotifier();
96
- notifyCompletedUploads;
97
- handleActiveStreamsChange;
98
- syncStatus;
99
- constructor(options) {
100
- super();
101
- this.options = options;
102
- this.activeStreams = options.subscriptions;
103
- this.logger = options.logger ?? Logger.get('PowerSyncStream');
104
- this.syncStatus = new SyncStatus({
105
- connected: false,
106
- connecting: false,
107
- lastSyncedAt: undefined,
108
- dataFlow: {
109
- uploading: false,
110
- downloading: false
111
- }
112
- });
113
- this.abortController = null;
114
- }
115
- triggerCrudUpload() {
116
- this.crudUploadNotifier.notify();
117
- }
118
- async waitForReady() { }
119
- waitForStatus(status) {
120
- return this.waitUntilStatusMatches((currentStatus) => {
121
- /**
122
- * Match only the partial status options provided in the
123
- * matching status
124
- */
125
- const matchPartialObject = (compA, compB) => {
126
- return Object.entries(compA).every(([key, value]) => {
127
- const comparisonBValue = compB[key];
128
- if (typeof value == 'object' && typeof comparisonBValue == 'object') {
129
- return matchPartialObject(value, comparisonBValue);
130
- }
131
- return value == comparisonBValue;
132
- });
133
- };
134
- return matchPartialObject(status, currentStatus);
135
- });
136
- }
137
- waitUntilStatusMatches(predicate) {
138
- return new Promise((resolve) => {
139
- if (predicate(this.syncStatus)) {
140
- resolve();
141
- return;
142
- }
143
- const l = this.registerListener({
144
- statusChanged: (updatedStatus) => {
145
- if (predicate(updatedStatus)) {
146
- resolve();
147
- l?.();
148
- }
149
- }
150
- });
151
- });
152
- }
153
- get lastSyncedAt() {
154
- const lastSynced = this.syncStatus.lastSyncedAt;
155
- return lastSynced && new Date(lastSynced);
156
- }
157
- get isConnected() {
158
- return this.syncStatus.connected;
159
- }
160
- async dispose() {
161
- super.dispose();
162
- this.crudUpdateListener?.();
163
- this.crudUpdateListener = undefined;
164
- }
165
- async getWriteCheckpoint() {
166
- const clientId = await this.options.adapter.getClientId();
167
- let path = `/write-checkpoint2.json?client_id=${clientId}`;
168
- const response = await this.options.remote.get(path);
169
- const checkpoint = response['data']['write_checkpoint'];
170
- this.logger.debug(`Created write checkpoint: ${checkpoint}`);
171
- return checkpoint;
172
- }
173
- async crudUploadLoop(signal) {
174
- while (!signal.aborted) {
175
- await Promise.all([
176
- // Start the initial CRUD upload on connect. Then, keep polling until we're done.
177
- this._uploadAllCrud(signal),
178
- this.delayRetry(signal, this.options.crudUploadThrottleMs)
179
- ]);
180
- await this.crudUploadNotifier.waitForNotification(signal);
181
- }
182
- }
183
- async _uploadAllCrud(signal) {
184
- return this.obtainLock({
185
- type: LockType.CRUD,
186
- callback: async () => {
187
- /**
188
- * Keep track of the first item in the CRUD queue for the last `uploadCrud` iteration.
189
- */
190
- let checkedCrudItem;
191
- while (!signal.aborted) {
192
- try {
193
- /**
194
- * This is the first item in the FIFO CRUD queue.
195
- */
196
- const nextCrudItem = await this.options.adapter.nextCrudItem();
197
- if (nextCrudItem) {
198
- this.updateSyncStatus({
199
- dataFlow: {
200
- uploading: true
201
- }
202
- });
203
- if (nextCrudItem.clientId == checkedCrudItem?.clientId) {
204
- // This will force a higher log level than exceptions which are caught here.
205
- this.logger.warn(`Potentially previously uploaded CRUD entries are still present in the upload queue.
206
- Make sure to handle uploads and complete CRUD transactions or batches by calling and awaiting their [.complete()] method.
207
- The next upload iteration will be delayed.`);
208
- throw new Error('Delaying due to previously encountered CRUD item.');
209
- }
210
- checkedCrudItem = nextCrudItem;
211
- await this.options.uploadCrud();
212
- this.updateSyncStatus({
213
- dataFlow: {
214
- uploadError: undefined
215
- }
216
- });
217
- }
218
- else {
219
- // Uploading is completed
220
- const neededUpdate = await this.options.adapter.updateLocalTarget(() => this.getWriteCheckpoint());
221
- if (neededUpdate) {
222
- this.notifyCompletedUploads?.();
223
- }
224
- else if (checkedCrudItem != null) {
225
- // Only log this if there was something to upload
226
- this.logger.debug('Upload complete, no write checkpoint needed.');
227
- }
228
- break;
229
- }
230
- }
231
- catch (ex) {
232
- checkedCrudItem = undefined;
233
- this.updateSyncStatus({
234
- dataFlow: {
235
- uploading: false,
236
- uploadError: ex
237
- }
238
- });
239
- await this.delayRetry(signal);
240
- if (!this.isConnected) {
241
- // Exit the upload loop if the sync stream is no longer connected
242
- break;
243
- }
244
- this.logger.debug(`Caught exception when uploading. Upload will retry after a delay. Exception: ${ex.message}`);
245
- }
246
- finally {
247
- this.updateSyncStatus({
248
- dataFlow: {
249
- uploading: false
250
- }
251
- });
252
- }
253
- }
254
- }
255
- });
256
- }
257
- async connect(options) {
258
- if (this.abortController) {
259
- await this.disconnect();
260
- }
261
- const controller = new AbortController();
262
- this.abortController = controller;
263
- this.streamingSyncPromise = Promise.all([
264
- this.crudUploadLoop(controller.signal).catch((ex) => this.logger.error('Error in crud upload loop', ex)),
265
- this.streamingSync(controller.signal, options)
266
- ]);
267
- // Return a promise that resolves when the connection status is updated to indicate that we're connected.
268
- return new Promise((resolve) => {
269
- const disposer = this.registerListener({
270
- statusChanged: (status) => {
271
- if (status.dataFlowStatus.downloadError != null) {
272
- this.logger.warn('Initial connect attempt did not successfully connect to server');
273
- }
274
- else if (status.connecting) {
275
- // Still connecting.
276
- return;
277
- }
278
- disposer();
279
- resolve();
280
- }
281
- });
282
- });
283
- }
284
- async disconnect() {
285
- if (!this.abortController) {
286
- return;
287
- }
288
- // This might be called multiple times
289
- if (!this.abortController.signal.aborted) {
290
- this.abortController.abort(new AbortOperation('Disconnect has been requested'));
291
- }
292
- // Await any pending operations before completing the disconnect operation
293
- try {
294
- await this.streamingSyncPromise;
295
- }
296
- catch (ex) {
297
- // The operation might have failed, all we care about is if it has completed
298
- this.logger.warn(ex);
299
- }
300
- this.streamingSyncPromise = undefined;
301
- this.abortController = null;
302
- this.updateSyncStatus({ connected: false, connecting: false });
303
- }
304
- async streamingSync(signal, options) {
305
- /**
306
- * Listen for CRUD updates and trigger upstream uploads
307
- */
308
- this.crudUpdateListener = this.options.adapter.registerListener({
309
- crudUpdate: () => this.triggerCrudUpload()
310
- });
311
- /**
312
- * Create a new abort controller which aborts items downstream.
313
- * This is needed to close any previous connections on exception.
314
- */
315
- let nestedAbortController = new AbortController();
316
- signal.addEventListener('abort', () => {
317
- /**
318
- * A request for disconnect was received upstream. Relay the request
319
- * to the nested abort controller.
320
- */
321
- nestedAbortController.abort(signal?.reason ?? new AbortOperation('Received command to disconnect from upstream'));
322
- this.crudUpdateListener?.();
323
- this.crudUpdateListener = undefined;
324
- this.updateSyncStatus({
325
- connected: false,
326
- connecting: false,
327
- dataFlow: {
328
- downloading: false,
329
- downloadProgress: null
330
- }
331
- });
332
- });
333
- /**
334
- * This loops runs until [retry] is false or the abort signal is set to aborted.
335
- * Aborting the nestedAbortController will:
336
- * - Abort any pending fetch requests
337
- * - Close any sync stream ReadableStreams (which will also close any established network requests)
338
- */
339
- while (true) {
340
- this.updateSyncStatus({ connecting: true });
341
- let shouldDelayRetry = true;
342
- let result = null;
343
- try {
344
- if (signal?.aborted) {
345
- break;
346
- }
347
- result = await this.streamingSyncIteration(nestedAbortController.signal, options);
348
- // Continue immediately, streamingSyncIteration will wait before completing if necessary.
349
- }
350
- catch (ex) {
351
- /**
352
- * Either:
353
- * - A network request failed with a failed connection or not OKAY response code.
354
- * - There was a sync processing error.
355
- * - The connection was aborted.
356
- * This loop will retry after a delay if the connection was not aborted.
357
- * The nested abort controller will cleanup any open network requests and streams.
358
- * The WebRemote should only abort pending fetch requests or close active Readable streams.
359
- */
360
- if (ex instanceof AbortOperation) {
361
- this.logger.warn(ex);
362
- shouldDelayRetry = false;
363
- // A disconnect was requested, we should not delay since there is no explicit retry
364
- }
365
- else if (this.connectionMayHaveChanged && ex.message?.indexOf('No iteration is active') >= 0) {
366
- this.connectionMayHaveChanged = false;
367
- this.logger.info('Sync error after changed connection, retrying immediately');
368
- shouldDelayRetry = false;
369
- }
370
- else {
371
- this.logger.error(ex);
372
- }
373
- this.updateSyncStatus({
374
- dataFlow: {
375
- downloadError: ex
376
- }
377
- });
378
- }
379
- finally {
380
- this.notifyCompletedUploads = undefined;
381
- if (!signal.aborted) {
382
- nestedAbortController.abort(new AbortOperation('Closing sync stream network requests before retry.'));
383
- nestedAbortController = new AbortController();
384
- }
385
- if (result?.immediateRestart != true) {
386
- this.updateSyncStatus({
387
- connected: false,
388
- connecting: true // May be unnecessary
389
- });
390
- // On error, wait a little before retrying
391
- if (shouldDelayRetry) {
392
- await this.delayRetry(nestedAbortController.signal);
393
- }
394
- }
395
- }
396
- }
397
- // Mark as disconnected if here
398
- this.updateSyncStatus({ connected: false, connecting: false });
399
- }
400
- markConnectionMayHaveChanged() {
401
- // By setting this field, we'll immediately retry if the next sync event causes an error triggered by us not having
402
- // an active sync iteration on the connection in use.
403
- this.connectionMayHaveChanged = true;
404
- // This triggers a `powersync_control` invocation if a sync iteration is currently active. This is a cheap call to
405
- // make when no subscriptions have actually changed, we're mainly interested in this immediately throwing if no
406
- // iteration is active. That allows us to reconnect ASAP, instead of having to wait for the next sync line.
407
- this.handleActiveStreamsChange?.();
408
- }
409
- /**
410
- * Older versions of the JS SDK used to encode subkeys as JSON in `OplogEntry.toJSON`.
411
- * Because subkeys are always strings, this leads to quotes being added around them in `ps_oplog`.
412
- * While this is not a problem as long as it's done consistently, it causes issues when a database
413
- * created by the JS SDK is used with other SDKs, or (more likely) when the new Rust sync client
414
- * is enabled.
415
- *
416
- * So, we add a migration from the old key format (with quotes) to the new one (no quotes). The
417
- * migration is only triggered when necessary (for now). The function returns whether the new format
418
- * should be used, so that the JS SDK is able to write to updated databases.
419
- *
420
- * @param requireFixedKeyFormat - Whether we require the new format or also support the old one.
421
- * The Rust client requires the new subkey format.
422
- * @returns Whether the database is now using the new, fixed subkey format.
423
- */
424
- async requireKeyFormat(requireFixedKeyFormat) {
425
- const hasMigrated = await this.options.adapter.hasMigratedSubkeys();
426
- if (requireFixedKeyFormat && !hasMigrated) {
427
- await this.options.adapter.migrateToFixedSubkeys();
428
- return true;
429
- }
430
- else {
431
- return hasMigrated;
432
- }
433
- }
434
- streamingSyncIteration(signal, options) {
435
- return this.obtainLock({
436
- type: LockType.SYNC,
437
- signal,
438
- callback: async () => {
439
- const resolvedOptions = {
440
- ...DEFAULT_STREAM_CONNECTION_OPTIONS,
441
- ...(options ?? {})
442
- };
443
- // Validate app metadata
444
- const invalidMetadata = Object.entries(resolvedOptions.appMetadata).filter(([_, value]) => typeof value != 'string');
445
- if (invalidMetadata.length > 0) {
446
- throw new Error(`Invalid appMetadata provided. Only string values are allowed. Invalid values: ${invalidMetadata.map(([key, value]) => `${key}: ${value}`).join(', ')}`);
447
- }
448
- await this.requireKeyFormat(true);
449
- return await this.rustSyncIteration(signal, resolvedOptions);
450
- }
451
- });
452
- }
453
- receiveSyncLines(data) {
454
- const { options, connection } = data;
455
- const remote = this.options.remote;
456
- const openInner = async () => {
457
- if (connection.connectionMethod == SyncStreamConnectionMethod.HTTP) {
458
- return await remote.fetchStream(options);
459
- }
460
- else {
461
- return await this.options.remote.socketStreamRaw({
462
- ...options,
463
- ...{ fetchStrategy: connection.fetchStrategy }
464
- });
465
- }
466
- };
467
- let inner;
468
- let done = false;
469
- return {
470
- async next() {
471
- if (done) {
472
- return doneResult;
473
- }
474
- else if (inner == null) {
475
- inner = await openInner();
476
- // We're connected here, so we can tell the core extension about it.
477
- return valueResult({
478
- command: PowerSyncControlCommand.CONNECTION_STATE,
479
- payload: 'established'
480
- });
481
- }
482
- else {
483
- const event = await inner.next();
484
- if (event.done) {
485
- done = true;
486
- return valueResult({ command: PowerSyncControlCommand.CONNECTION_STATE, payload: 'end' });
487
- }
488
- else {
489
- return valueResult({
490
- command: typeof event.value == 'string'
491
- ? PowerSyncControlCommand.PROCESS_TEXT_LINE
492
- : PowerSyncControlCommand.PROCESS_BSON_LINE,
493
- payload: event.value
494
- });
495
- }
496
- }
497
- }
498
- };
499
- }
500
- async rustSyncIteration(signal, resolvedOptions) {
501
- const syncImplementation = this;
502
- const adapter = this.options.adapter;
503
- const remote = this.options.remote;
504
- let hideDisconnectOnRestart = false;
505
- let notifyTokenRefreshed;
506
- if (signal.aborted) {
507
- throw new AbortOperation('Connection request has been aborted');
508
- }
509
- function startCommand() {
510
- const options = {
511
- parameters: resolvedOptions.params,
512
- app_metadata: resolvedOptions.appMetadata,
513
- active_streams: syncImplementation.activeStreams,
514
- include_defaults: resolvedOptions.includeDefaultStreams
515
- };
516
- if (resolvedOptions.serializedSchema) {
517
- options.schema = resolvedOptions.serializedSchema;
518
- }
519
- return invokePowerSyncControl(PowerSyncControlCommand.START, JSON.stringify(options));
520
- }
521
- async function stop() {
522
- const instructions = await invokePowerSyncControl(PowerSyncControlCommand.STOP);
523
- for (const instruction of instructions) {
524
- // We don't need to handle interrupting instructions since we're unconditionally ending the sync iteration at
525
- // this point.
526
- if (isInterruptingInstruction(instruction))
527
- continue;
528
- await handleInstruction(instruction);
529
- }
530
- }
531
- async function invokePowerSyncControl(op, payload) {
532
- const rawResponse = await adapter.control(op, payload ?? null);
533
- const logger = syncImplementation.logger;
534
- logger.trace('powersync_control', op, payload == null || typeof payload == 'string' ? payload : '<bytes>', rawResponse);
535
- if (op != PowerSyncControlCommand.STOP) {
536
- // Evidently we have a working connection here, otherwise powersync_control would have failed.
537
- syncImplementation.connectionMayHaveChanged = false;
538
- }
539
- return JSON.parse(rawResponse);
540
- }
541
- async function handleInstruction(instruction) {
542
- if ('LogLine' in instruction) {
543
- switch (instruction.LogLine.severity) {
544
- case 'DEBUG':
545
- syncImplementation.logger.debug(instruction.LogLine.line);
546
- break;
547
- case 'INFO':
548
- syncImplementation.logger.info(instruction.LogLine.line);
549
- break;
550
- case 'WARNING':
551
- syncImplementation.logger.warn(instruction.LogLine.line);
552
- break;
553
- }
554
- }
555
- else if ('UpdateSyncStatus' in instruction) {
556
- syncImplementation.updateSyncStatus(coreStatusToJs(instruction.UpdateSyncStatus.status));
557
- }
558
- else if ('FetchCredentials' in instruction) {
559
- if (instruction.FetchCredentials.did_expire) {
560
- remote.invalidateCredentials();
561
- }
562
- else {
563
- remote.invalidateCredentials();
564
- // Restart iteration after the credentials have been refreshed.
565
- remote.fetchCredentials().then((_) => {
566
- notifyTokenRefreshed?.();
567
- }, (err) => {
568
- syncImplementation.logger.warn('Could not prefetch credentials', err);
569
- });
570
- }
571
- }
572
- else if ('FlushFileSystem' in instruction) {
573
- // Not necessary on JS platforms.
574
- }
575
- else if ('DidCompleteSync' in instruction) {
576
- syncImplementation.updateSyncStatus({
577
- dataFlow: {
578
- downloadError: undefined
579
- }
580
- });
581
- }
582
- }
583
- try {
584
- const defaultResult = { immediateRestart: false };
585
- // Pending sync lines received from the service, as well as local events that trigger a powersync_control
586
- // invocation (local events include refreshed tokens and completed uploads).
587
- // This is a single data stream so that we can handle all control calls from a single place.
588
- let controlInvocations = null;
589
- for (const startInstruction of await startCommand()) {
590
- if ('EstablishSyncStream' in startInstruction) {
591
- const syncOptions = {
592
- path: '/sync/stream',
593
- abortSignal: signal,
594
- data: startInstruction.EstablishSyncStream.request
595
- };
596
- controlInvocations = injectable(syncImplementation.receiveSyncLines({
597
- options: syncOptions,
598
- connection: resolvedOptions
599
- }));
600
- }
601
- else if ('CloseSyncStream' in startInstruction) {
602
- return defaultResult;
603
- }
604
- else {
605
- await handleInstruction(startInstruction);
606
- }
607
- }
608
- if (controlInvocations == null)
609
- return defaultResult;
610
- this.notifyCompletedUploads = () => {
611
- controlInvocations.inject({ command: PowerSyncControlCommand.NOTIFY_CRUD_UPLOAD_COMPLETED });
612
- };
613
- this.handleActiveStreamsChange = () => {
614
- controlInvocations.inject({
615
- command: PowerSyncControlCommand.UPDATE_SUBSCRIPTIONS,
616
- payload: JSON.stringify(this.activeStreams)
617
- });
618
- };
619
- notifyTokenRefreshed = () => {
620
- controlInvocations.inject({
621
- command: PowerSyncControlCommand.NOTIFY_TOKEN_REFRESHED
622
- });
623
- };
624
- let hadSyncLine = false;
625
- loop: while (true) {
626
- const { done, value } = await controlInvocations.next();
627
- if (done)
628
- break;
629
- if (!hadSyncLine) {
630
- // Trigger a local CRUD upload when the first sync line has been received, this allows uploading local changes
631
- // that have been made while offline or disconnected.
632
- if (value.command == PowerSyncControlCommand.PROCESS_TEXT_LINE ||
633
- value.command == PowerSyncControlCommand.PROCESS_BSON_LINE) {
634
- hadSyncLine = true;
635
- this.triggerCrudUpload?.();
636
- }
637
- }
638
- const instructions = await invokePowerSyncControl(value.command, value.payload);
639
- for (const instruction of instructions) {
640
- if ('EstablishSyncStream' in instruction) {
641
- throw new Error('Received EstablishSyncStream while already connected.');
642
- }
643
- else if ('CloseSyncStream' in instruction) {
644
- hideDisconnectOnRestart = instruction.CloseSyncStream.hide_disconnect;
645
- break loop;
646
- }
647
- else {
648
- await handleInstruction(instruction);
649
- }
650
- }
651
- }
652
- }
653
- finally {
654
- this.notifyCompletedUploads = this.handleActiveStreamsChange = undefined;
655
- notifyTokenRefreshed = undefined;
656
- await stop();
657
- }
658
- return { immediateRestart: hideDisconnectOnRestart };
659
- }
660
- updateSyncStatus(options) {
661
- const updatedStatus = new SyncStatus({
662
- connected: options.connected ?? this.syncStatus.connected,
663
- connecting: !options.connected && (options.connecting ?? this.syncStatus.connecting),
664
- lastSyncedAt: options.lastSyncedAt ?? this.syncStatus.lastSyncedAt,
665
- dataFlow: {
666
- ...this.syncStatus.dataFlowStatus,
667
- ...options.dataFlow
668
- },
669
- priorityStatusEntries: options.priorityStatusEntries ?? this.syncStatus.priorityStatusEntries,
670
- clientImplementation: options.clientImplementation ?? this.syncStatus.clientImplementation
671
- });
672
- if (!this.syncStatus.isEqual(updatedStatus)) {
673
- this.syncStatus = updatedStatus;
674
- // Only trigger this is there was a change
675
- this.iterateListeners((cb) => cb.statusChanged?.(updatedStatus));
676
- }
677
- // trigger this for all updates
678
- this.iterateListeners((cb) => cb.statusUpdated?.(options));
679
- }
680
- async delayRetry(signal, delay = this.options.retryDelayMs) {
681
- return new Promise((resolve) => {
682
- if (signal?.aborted) {
683
- // If the signal is already aborted, resolve immediately
684
- resolve();
685
- return;
686
- }
687
- let timeoutId;
688
- const endDelay = () => {
689
- resolve();
690
- if (timeoutId) {
691
- clearTimeout(timeoutId);
692
- timeoutId = undefined;
693
- }
694
- signal?.removeEventListener('abort', endDelay);
695
- };
696
- signal?.addEventListener('abort', endDelay, { once: true });
697
- timeoutId = setTimeout(endDelay, delay);
698
- });
699
- }
700
- updateSubscriptions(subscriptions) {
701
- this.activeStreams = subscriptions;
702
- this.handleActiveStreamsChange?.();
703
- }
704
- }
705
- //# sourceMappingURL=AbstractStreamingSyncImplementation.js.map