@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,706 +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
- signal,
187
- callback: async () => {
188
- /**
189
- * Keep track of the first item in the CRUD queue for the last `uploadCrud` iteration.
190
- */
191
- let checkedCrudItem;
192
- while (!signal.aborted) {
193
- try {
194
- /**
195
- * This is the first item in the FIFO CRUD queue.
196
- */
197
- const nextCrudItem = await this.options.adapter.nextCrudItem();
198
- if (nextCrudItem) {
199
- this.updateSyncStatus({
200
- dataFlow: {
201
- uploading: true
202
- }
203
- });
204
- if (nextCrudItem.clientId == checkedCrudItem?.clientId) {
205
- // This will force a higher log level than exceptions which are caught here.
206
- this.logger.warn(`Potentially previously uploaded CRUD entries are still present in the upload queue.
207
- Make sure to handle uploads and complete CRUD transactions or batches by calling and awaiting their [.complete()] method.
208
- The next upload iteration will be delayed.`);
209
- throw new Error('Delaying due to previously encountered CRUD item.');
210
- }
211
- checkedCrudItem = nextCrudItem;
212
- await this.options.uploadCrud();
213
- this.updateSyncStatus({
214
- dataFlow: {
215
- uploadError: undefined
216
- }
217
- });
218
- }
219
- else {
220
- // Uploading is completed
221
- const neededUpdate = await this.options.adapter.updateLocalTarget(() => this.getWriteCheckpoint());
222
- if (neededUpdate) {
223
- this.notifyCompletedUploads?.();
224
- }
225
- else if (checkedCrudItem != null) {
226
- // Only log this if there was something to upload
227
- this.logger.debug('Upload complete, no write checkpoint needed.');
228
- }
229
- break;
230
- }
231
- }
232
- catch (ex) {
233
- checkedCrudItem = undefined;
234
- this.updateSyncStatus({
235
- dataFlow: {
236
- uploading: false,
237
- uploadError: ex
238
- }
239
- });
240
- await this.delayRetry(signal);
241
- if (!this.isConnected) {
242
- // Exit the upload loop if the sync stream is no longer connected
243
- break;
244
- }
245
- this.logger.debug(`Caught exception when uploading. Upload will retry after a delay. Exception: ${ex.message}`);
246
- }
247
- finally {
248
- this.updateSyncStatus({
249
- dataFlow: {
250
- uploading: false
251
- }
252
- });
253
- }
254
- }
255
- }
256
- });
257
- }
258
- async connect(options) {
259
- if (this.abortController) {
260
- await this.disconnect();
261
- }
262
- const controller = new AbortController();
263
- this.abortController = controller;
264
- this.streamingSyncPromise = Promise.all([
265
- this.crudUploadLoop(controller.signal).catch((ex) => this.logger.error('Error in crud upload loop', ex)),
266
- this.streamingSync(controller.signal, options)
267
- ]);
268
- // Return a promise that resolves when the connection status is updated to indicate that we're connected.
269
- return new Promise((resolve) => {
270
- const disposer = this.registerListener({
271
- statusChanged: (status) => {
272
- if (status.dataFlowStatus.downloadError != null) {
273
- this.logger.warn('Initial connect attempt did not successfully connect to server');
274
- }
275
- else if (status.connecting) {
276
- // Still connecting.
277
- return;
278
- }
279
- disposer();
280
- resolve();
281
- }
282
- });
283
- });
284
- }
285
- async disconnect() {
286
- if (!this.abortController) {
287
- return;
288
- }
289
- // This might be called multiple times
290
- if (!this.abortController.signal.aborted) {
291
- this.abortController.abort(new AbortOperation('Disconnect has been requested'));
292
- }
293
- // Await any pending operations before completing the disconnect operation
294
- try {
295
- await this.streamingSyncPromise;
296
- }
297
- catch (ex) {
298
- // The operation might have failed, all we care about is if it has completed
299
- this.logger.warn(ex);
300
- }
301
- this.streamingSyncPromise = undefined;
302
- this.abortController = null;
303
- this.updateSyncStatus({ connected: false, connecting: false });
304
- }
305
- async streamingSync(signal, options) {
306
- /**
307
- * Listen for CRUD updates and trigger upstream uploads
308
- */
309
- this.crudUpdateListener = this.options.adapter.registerListener({
310
- crudUpdate: () => this.triggerCrudUpload()
311
- });
312
- /**
313
- * Create a new abort controller which aborts items downstream.
314
- * This is needed to close any previous connections on exception.
315
- */
316
- let nestedAbortController = new AbortController();
317
- signal.addEventListener('abort', () => {
318
- /**
319
- * A request for disconnect was received upstream. Relay the request
320
- * to the nested abort controller.
321
- */
322
- nestedAbortController.abort(signal?.reason ?? new AbortOperation('Received command to disconnect from upstream'));
323
- this.crudUpdateListener?.();
324
- this.crudUpdateListener = undefined;
325
- this.updateSyncStatus({
326
- connected: false,
327
- connecting: false,
328
- dataFlow: {
329
- downloading: false,
330
- downloadProgress: null
331
- }
332
- });
333
- });
334
- /**
335
- * This loops runs until [retry] is false or the abort signal is set to aborted.
336
- * Aborting the nestedAbortController will:
337
- * - Abort any pending fetch requests
338
- * - Close any sync stream ReadableStreams (which will also close any established network requests)
339
- */
340
- while (true) {
341
- this.updateSyncStatus({ connecting: true });
342
- let shouldDelayRetry = true;
343
- let result = null;
344
- try {
345
- if (signal?.aborted) {
346
- break;
347
- }
348
- result = await this.streamingSyncIteration(nestedAbortController.signal, options);
349
- // Continue immediately, streamingSyncIteration will wait before completing if necessary.
350
- }
351
- catch (ex) {
352
- /**
353
- * Either:
354
- * - A network request failed with a failed connection or not OKAY response code.
355
- * - There was a sync processing error.
356
- * - The connection was aborted.
357
- * This loop will retry after a delay if the connection was not aborted.
358
- * The nested abort controller will cleanup any open network requests and streams.
359
- * The WebRemote should only abort pending fetch requests or close active Readable streams.
360
- */
361
- if (ex instanceof AbortOperation) {
362
- this.logger.warn(ex);
363
- shouldDelayRetry = false;
364
- // A disconnect was requested, we should not delay since there is no explicit retry
365
- }
366
- else if (this.connectionMayHaveChanged && ex.message?.indexOf('No iteration is active') >= 0) {
367
- this.connectionMayHaveChanged = false;
368
- this.logger.info('Sync error after changed connection, retrying immediately');
369
- shouldDelayRetry = false;
370
- }
371
- else {
372
- this.logger.error(ex);
373
- }
374
- this.updateSyncStatus({
375
- dataFlow: {
376
- downloadError: ex
377
- }
378
- });
379
- }
380
- finally {
381
- this.notifyCompletedUploads = undefined;
382
- if (!signal.aborted) {
383
- nestedAbortController.abort(new AbortOperation('Closing sync stream network requests before retry.'));
384
- nestedAbortController = new AbortController();
385
- }
386
- if (result?.immediateRestart != true) {
387
- this.updateSyncStatus({
388
- connected: false,
389
- connecting: true // May be unnecessary
390
- });
391
- // On error, wait a little before retrying
392
- if (shouldDelayRetry) {
393
- await this.delayRetry(nestedAbortController.signal);
394
- }
395
- }
396
- }
397
- }
398
- // Mark as disconnected if here
399
- this.updateSyncStatus({ connected: false, connecting: false });
400
- }
401
- markConnectionMayHaveChanged() {
402
- // By setting this field, we'll immediately retry if the next sync event causes an error triggered by us not having
403
- // an active sync iteration on the connection in use.
404
- this.connectionMayHaveChanged = true;
405
- // This triggers a `powersync_control` invocation if a sync iteration is currently active. This is a cheap call to
406
- // make when no subscriptions have actually changed, we're mainly interested in this immediately throwing if no
407
- // iteration is active. That allows us to reconnect ASAP, instead of having to wait for the next sync line.
408
- this.handleActiveStreamsChange?.();
409
- }
410
- /**
411
- * Older versions of the JS SDK used to encode subkeys as JSON in `OplogEntry.toJSON`.
412
- * Because subkeys are always strings, this leads to quotes being added around them in `ps_oplog`.
413
- * While this is not a problem as long as it's done consistently, it causes issues when a database
414
- * created by the JS SDK is used with other SDKs, or (more likely) when the new Rust sync client
415
- * is enabled.
416
- *
417
- * So, we add a migration from the old key format (with quotes) to the new one (no quotes). The
418
- * migration is only triggered when necessary (for now). The function returns whether the new format
419
- * should be used, so that the JS SDK is able to write to updated databases.
420
- *
421
- * @param requireFixedKeyFormat - Whether we require the new format or also support the old one.
422
- * The Rust client requires the new subkey format.
423
- * @returns Whether the database is now using the new, fixed subkey format.
424
- */
425
- async requireKeyFormat(requireFixedKeyFormat) {
426
- const hasMigrated = await this.options.adapter.hasMigratedSubkeys();
427
- if (requireFixedKeyFormat && !hasMigrated) {
428
- await this.options.adapter.migrateToFixedSubkeys();
429
- return true;
430
- }
431
- else {
432
- return hasMigrated;
433
- }
434
- }
435
- streamingSyncIteration(signal, options) {
436
- return this.obtainLock({
437
- type: LockType.SYNC,
438
- signal,
439
- callback: async () => {
440
- const resolvedOptions = {
441
- ...DEFAULT_STREAM_CONNECTION_OPTIONS,
442
- ...(options ?? {})
443
- };
444
- // Validate app metadata
445
- const invalidMetadata = Object.entries(resolvedOptions.appMetadata).filter(([_, value]) => typeof value != 'string');
446
- if (invalidMetadata.length > 0) {
447
- throw new Error(`Invalid appMetadata provided. Only string values are allowed. Invalid values: ${invalidMetadata.map(([key, value]) => `${key}: ${value}`).join(', ')}`);
448
- }
449
- await this.requireKeyFormat(true);
450
- return await this.rustSyncIteration(signal, resolvedOptions);
451
- }
452
- });
453
- }
454
- receiveSyncLines(data) {
455
- const { options, connection } = data;
456
- const remote = this.options.remote;
457
- const openInner = async () => {
458
- if (connection.connectionMethod == SyncStreamConnectionMethod.HTTP) {
459
- return await remote.fetchStream(options);
460
- }
461
- else {
462
- return await this.options.remote.socketStreamRaw({
463
- ...options,
464
- ...{ fetchStrategy: connection.fetchStrategy }
465
- });
466
- }
467
- };
468
- let inner;
469
- let done = false;
470
- return {
471
- async next() {
472
- if (done) {
473
- return doneResult;
474
- }
475
- else if (inner == null) {
476
- inner = await openInner();
477
- // We're connected here, so we can tell the core extension about it.
478
- return valueResult({
479
- command: PowerSyncControlCommand.CONNECTION_STATE,
480
- payload: 'established'
481
- });
482
- }
483
- else {
484
- const event = await inner.next();
485
- if (event.done) {
486
- done = true;
487
- return valueResult({ command: PowerSyncControlCommand.CONNECTION_STATE, payload: 'end' });
488
- }
489
- else {
490
- return valueResult({
491
- command: typeof event.value == 'string'
492
- ? PowerSyncControlCommand.PROCESS_TEXT_LINE
493
- : PowerSyncControlCommand.PROCESS_BSON_LINE,
494
- payload: event.value
495
- });
496
- }
497
- }
498
- }
499
- };
500
- }
501
- async rustSyncIteration(signal, resolvedOptions) {
502
- const syncImplementation = this;
503
- const adapter = this.options.adapter;
504
- const remote = this.options.remote;
505
- let hideDisconnectOnRestart = false;
506
- let notifyTokenRefreshed;
507
- if (signal.aborted) {
508
- throw new AbortOperation('Connection request has been aborted');
509
- }
510
- function startCommand() {
511
- const options = {
512
- parameters: resolvedOptions.params,
513
- app_metadata: resolvedOptions.appMetadata,
514
- active_streams: syncImplementation.activeStreams,
515
- include_defaults: resolvedOptions.includeDefaultStreams
516
- };
517
- if (resolvedOptions.serializedSchema) {
518
- options.schema = resolvedOptions.serializedSchema;
519
- }
520
- return invokePowerSyncControl(PowerSyncControlCommand.START, JSON.stringify(options));
521
- }
522
- async function stop() {
523
- const instructions = await invokePowerSyncControl(PowerSyncControlCommand.STOP);
524
- for (const instruction of instructions) {
525
- // We don't need to handle interrupting instructions since we're unconditionally ending the sync iteration at
526
- // this point.
527
- if (isInterruptingInstruction(instruction))
528
- continue;
529
- await handleInstruction(instruction);
530
- }
531
- }
532
- async function invokePowerSyncControl(op, payload) {
533
- const rawResponse = await adapter.control(op, payload ?? null);
534
- const logger = syncImplementation.logger;
535
- logger.trace('powersync_control', op, payload == null || typeof payload == 'string' ? payload : '<bytes>', rawResponse);
536
- if (op != PowerSyncControlCommand.STOP) {
537
- // Evidently we have a working connection here, otherwise powersync_control would have failed.
538
- syncImplementation.connectionMayHaveChanged = false;
539
- }
540
- return JSON.parse(rawResponse);
541
- }
542
- async function handleInstruction(instruction) {
543
- if ('LogLine' in instruction) {
544
- switch (instruction.LogLine.severity) {
545
- case 'DEBUG':
546
- syncImplementation.logger.debug(instruction.LogLine.line);
547
- break;
548
- case 'INFO':
549
- syncImplementation.logger.info(instruction.LogLine.line);
550
- break;
551
- case 'WARNING':
552
- syncImplementation.logger.warn(instruction.LogLine.line);
553
- break;
554
- }
555
- }
556
- else if ('UpdateSyncStatus' in instruction) {
557
- syncImplementation.updateSyncStatus(coreStatusToJs(instruction.UpdateSyncStatus.status));
558
- }
559
- else if ('FetchCredentials' in instruction) {
560
- if (instruction.FetchCredentials.did_expire) {
561
- remote.invalidateCredentials();
562
- }
563
- else {
564
- remote.invalidateCredentials();
565
- // Restart iteration after the credentials have been refreshed.
566
- remote.fetchCredentials().then((_) => {
567
- notifyTokenRefreshed?.();
568
- }, (err) => {
569
- syncImplementation.logger.warn('Could not prefetch credentials', err);
570
- });
571
- }
572
- }
573
- else if ('FlushFileSystem' in instruction) {
574
- // Not necessary on JS platforms.
575
- }
576
- else if ('DidCompleteSync' in instruction) {
577
- syncImplementation.updateSyncStatus({
578
- dataFlow: {
579
- downloadError: undefined
580
- }
581
- });
582
- }
583
- }
584
- try {
585
- const defaultResult = { immediateRestart: false };
586
- // Pending sync lines received from the service, as well as local events that trigger a powersync_control
587
- // invocation (local events include refreshed tokens and completed uploads).
588
- // This is a single data stream so that we can handle all control calls from a single place.
589
- let controlInvocations = null;
590
- for (const startInstruction of await startCommand()) {
591
- if ('EstablishSyncStream' in startInstruction) {
592
- const syncOptions = {
593
- path: '/sync/stream',
594
- abortSignal: signal,
595
- data: startInstruction.EstablishSyncStream.request
596
- };
597
- controlInvocations = injectable(syncImplementation.receiveSyncLines({
598
- options: syncOptions,
599
- connection: resolvedOptions
600
- }));
601
- }
602
- else if ('CloseSyncStream' in startInstruction) {
603
- return defaultResult;
604
- }
605
- else {
606
- await handleInstruction(startInstruction);
607
- }
608
- }
609
- if (controlInvocations == null)
610
- return defaultResult;
611
- this.notifyCompletedUploads = () => {
612
- controlInvocations.inject({ command: PowerSyncControlCommand.NOTIFY_CRUD_UPLOAD_COMPLETED });
613
- };
614
- this.handleActiveStreamsChange = () => {
615
- controlInvocations.inject({
616
- command: PowerSyncControlCommand.UPDATE_SUBSCRIPTIONS,
617
- payload: JSON.stringify(this.activeStreams)
618
- });
619
- };
620
- notifyTokenRefreshed = () => {
621
- controlInvocations.inject({
622
- command: PowerSyncControlCommand.NOTIFY_TOKEN_REFRESHED
623
- });
624
- };
625
- let hadSyncLine = false;
626
- loop: while (true) {
627
- const { done, value } = await controlInvocations.next();
628
- if (done)
629
- break;
630
- if (!hadSyncLine) {
631
- // Trigger a local CRUD upload when the first sync line has been received, this allows uploading local changes
632
- // that have been made while offline or disconnected.
633
- if (value.command == PowerSyncControlCommand.PROCESS_TEXT_LINE ||
634
- value.command == PowerSyncControlCommand.PROCESS_BSON_LINE) {
635
- hadSyncLine = true;
636
- this.triggerCrudUpload?.();
637
- }
638
- }
639
- const instructions = await invokePowerSyncControl(value.command, value.payload);
640
- for (const instruction of instructions) {
641
- if ('EstablishSyncStream' in instruction) {
642
- throw new Error('Received EstablishSyncStream while already connected.');
643
- }
644
- else if ('CloseSyncStream' in instruction) {
645
- hideDisconnectOnRestart = instruction.CloseSyncStream.hide_disconnect;
646
- break loop;
647
- }
648
- else {
649
- await handleInstruction(instruction);
650
- }
651
- }
652
- }
653
- }
654
- finally {
655
- this.notifyCompletedUploads = this.handleActiveStreamsChange = undefined;
656
- notifyTokenRefreshed = undefined;
657
- await stop();
658
- }
659
- return { immediateRestart: hideDisconnectOnRestart };
660
- }
661
- updateSyncStatus(options) {
662
- const updatedStatus = new SyncStatus({
663
- connected: options.connected ?? this.syncStatus.connected,
664
- connecting: !options.connected && (options.connecting ?? this.syncStatus.connecting),
665
- lastSyncedAt: options.lastSyncedAt ?? this.syncStatus.lastSyncedAt,
666
- dataFlow: {
667
- ...this.syncStatus.dataFlowStatus,
668
- ...options.dataFlow
669
- },
670
- priorityStatusEntries: options.priorityStatusEntries ?? this.syncStatus.priorityStatusEntries,
671
- clientImplementation: options.clientImplementation ?? this.syncStatus.clientImplementation
672
- });
673
- if (!this.syncStatus.isEqual(updatedStatus)) {
674
- this.syncStatus = updatedStatus;
675
- // Only trigger this is there was a change
676
- this.iterateListeners((cb) => cb.statusChanged?.(updatedStatus));
677
- }
678
- // trigger this for all updates
679
- this.iterateListeners((cb) => cb.statusUpdated?.(options));
680
- }
681
- async delayRetry(signal, delay = this.options.retryDelayMs) {
682
- return new Promise((resolve) => {
683
- if (signal?.aborted) {
684
- // If the signal is already aborted, resolve immediately
685
- resolve();
686
- return;
687
- }
688
- let timeoutId;
689
- const endDelay = () => {
690
- resolve();
691
- if (timeoutId) {
692
- clearTimeout(timeoutId);
693
- timeoutId = undefined;
694
- }
695
- signal?.removeEventListener('abort', endDelay);
696
- };
697
- signal?.addEventListener('abort', endDelay, { once: true });
698
- timeoutId = setTimeout(endDelay, delay);
699
- });
700
- }
701
- updateSubscriptions(subscriptions) {
702
- this.activeStreams = subscriptions;
703
- this.handleActiveStreamsChange?.();
704
- }
705
- }
706
- //# sourceMappingURL=AbstractStreamingSyncImplementation.js.map