@tanstack/powersync-db-collection 0.1.67 → 0.2.1
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.
- package/dist/cjs/PowerSyncTransactor.cjs +31 -18
- package/dist/cjs/PowerSyncTransactor.cjs.map +1 -1
- package/dist/cjs/PowerSyncTransactor.d.cts +4 -4
- package/dist/cjs/definitions.cjs.map +1 -1
- package/dist/cjs/definitions.d.cts +4 -4
- package/dist/cjs/helpers.cjs.map +1 -1
- package/dist/cjs/helpers.d.cts +6 -9
- package/dist/cjs/index.d.cts +1 -0
- package/dist/cjs/powersync.cjs +254 -82
- package/dist/cjs/powersync.cjs.map +1 -1
- package/dist/cjs/serialization.cjs.map +1 -1
- package/dist/cjs/serialization.d.cts +2 -6
- package/dist/esm/PowerSyncTransactor.d.ts +4 -4
- package/dist/esm/PowerSyncTransactor.js +32 -19
- package/dist/esm/PowerSyncTransactor.js.map +1 -1
- package/dist/esm/definitions.d.ts +4 -4
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/helpers.d.ts +6 -9
- package/dist/esm/helpers.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/powersync.js +255 -83
- package/dist/esm/powersync.js.map +1 -1
- package/dist/esm/serialization.d.ts +2 -6
- package/dist/esm/serialization.js.map +1 -1
- package/package.json +7 -6
- package/src/PowerSyncTransactor.ts +46 -25
- package/src/definitions.ts +11 -5
- package/src/helpers.ts +10 -18
- package/src/index.ts +1 -0
- package/src/powersync.ts +316 -101
- package/src/serialization.ts +8 -17
package/dist/cjs/powersync.cjs
CHANGED
|
@@ -9,6 +9,9 @@ const definitions = require("./definitions.cjs");
|
|
|
9
9
|
const helpers = require("./helpers.cjs");
|
|
10
10
|
const schema = require("./schema.cjs");
|
|
11
11
|
const serialization = require("./serialization.cjs");
|
|
12
|
+
function isPromiseLike(value) {
|
|
13
|
+
return !!value && (typeof value === `object` || typeof value === `function`) && typeof value.then === `function`;
|
|
14
|
+
}
|
|
12
15
|
function powerSyncCollectionOptions(config) {
|
|
13
16
|
const outputConfig = createPowerSyncCollectionConfig(config);
|
|
14
17
|
return db.withCollectionConfigFactory(
|
|
@@ -36,12 +39,15 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
36
39
|
return validation.value;
|
|
37
40
|
} else if (`issues` in validation) {
|
|
38
41
|
const issueMessage = `Failed to validate incoming data for ${viewName}. Issues: ${validation.issues.map((issue) => `${issue.path} - ${issue.message}`)}`;
|
|
39
|
-
database.logger.error
|
|
42
|
+
database.logger.log({ level: common.LogLevels.error, message: issueMessage });
|
|
40
43
|
onDeserializationError(validation);
|
|
41
44
|
throw new Error(issueMessage);
|
|
42
45
|
} else {
|
|
43
46
|
const unknownErrorMessage = `Unknown deserialization error for ${viewName}`;
|
|
44
|
-
database.logger.
|
|
47
|
+
database.logger.log({
|
|
48
|
+
level: common.LogLevels.error,
|
|
49
|
+
message: unknownErrorMessage
|
|
50
|
+
});
|
|
45
51
|
onDeserializationError({ issues: [{ message: unknownErrorMessage }] });
|
|
46
52
|
throw new Error(unknownErrorMessage);
|
|
47
53
|
}
|
|
@@ -60,6 +66,8 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
60
66
|
const abortController = new AbortController();
|
|
61
67
|
let disposeTracking = null;
|
|
62
68
|
let trackingSetup = null;
|
|
69
|
+
let activeTrackingDisposal = null;
|
|
70
|
+
let abortTrackingDisposal = null;
|
|
63
71
|
if (syncMode === `eager`) {
|
|
64
72
|
return runEagerSync();
|
|
65
73
|
} else {
|
|
@@ -72,10 +80,29 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
72
80
|
}
|
|
73
81
|
const dispose = disposeTracking;
|
|
74
82
|
if (!dispose) {
|
|
83
|
+
await activeTrackingDisposal;
|
|
75
84
|
return;
|
|
76
85
|
}
|
|
77
86
|
disposeTracking = null;
|
|
78
|
-
|
|
87
|
+
let disposal;
|
|
88
|
+
try {
|
|
89
|
+
disposal = Promise.resolve(dispose(context ? { context } : void 0));
|
|
90
|
+
} catch (error) {
|
|
91
|
+
disposal = Promise.reject(error);
|
|
92
|
+
}
|
|
93
|
+
activeTrackingDisposal = disposal;
|
|
94
|
+
try {
|
|
95
|
+
await disposal;
|
|
96
|
+
} finally {
|
|
97
|
+
if (activeTrackingDisposal === disposal) {
|
|
98
|
+
activeTrackingDisposal = null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function disposeTrackingAfterAbort() {
|
|
103
|
+
const disposal = abortTrackingDisposal ??= safelyDisposeTracking();
|
|
104
|
+
void disposal.catch(() => void 0);
|
|
105
|
+
return disposal;
|
|
79
106
|
}
|
|
80
107
|
async function establishTracking(options, appliedReceipts) {
|
|
81
108
|
const setup = (async () => {
|
|
@@ -96,6 +123,7 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
96
123
|
return await database.triggers.createDiffTrigger({
|
|
97
124
|
source: viewName,
|
|
98
125
|
destination: trackedTableName,
|
|
126
|
+
columns: table.columns.map((column) => column.name),
|
|
99
127
|
setupContext,
|
|
100
128
|
when,
|
|
101
129
|
hooks: {
|
|
@@ -119,9 +147,10 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
119
147
|
}
|
|
120
148
|
appliedReceipts.push(commit());
|
|
121
149
|
}
|
|
122
|
-
database.logger.
|
|
123
|
-
|
|
124
|
-
|
|
150
|
+
database.logger.log({
|
|
151
|
+
level: common.LogLevels.info,
|
|
152
|
+
message: `Sync is ready for ${viewName} into ${trackedTableName}`
|
|
153
|
+
});
|
|
125
154
|
}
|
|
126
155
|
}
|
|
127
156
|
});
|
|
@@ -138,10 +167,11 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
138
167
|
await database.writeTransaction(async (context) => {
|
|
139
168
|
await flushDiffRecordsWithContext(context, ignoredReceipts);
|
|
140
169
|
}).catch((error) => {
|
|
141
|
-
database.logger.
|
|
142
|
-
|
|
170
|
+
database.logger.log({
|
|
171
|
+
level: common.LogLevels.error,
|
|
172
|
+
message: `An error has been detected in the sync handler`,
|
|
143
173
|
error
|
|
144
|
-
);
|
|
174
|
+
});
|
|
145
175
|
});
|
|
146
176
|
}
|
|
147
177
|
async function flushDiffRecordsWithContext(context, appliedReceipts) {
|
|
@@ -181,16 +211,18 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
181
211
|
appliedReceipts.push(applied);
|
|
182
212
|
pendingOperationStore.resolvePendingFor(pendingOperations);
|
|
183
213
|
} catch (error) {
|
|
184
|
-
database.logger.
|
|
185
|
-
|
|
214
|
+
database.logger.log({
|
|
215
|
+
level: common.LogLevels.error,
|
|
216
|
+
message: `An error has been detected in the sync handler`,
|
|
186
217
|
error
|
|
187
|
-
);
|
|
218
|
+
});
|
|
188
219
|
}
|
|
189
220
|
}
|
|
190
221
|
async function start(afterOnChangeRegistered) {
|
|
191
|
-
database.logger.
|
|
192
|
-
|
|
193
|
-
|
|
222
|
+
database.logger.log({
|
|
223
|
+
level: common.LogLevels.info,
|
|
224
|
+
message: `Sync is starting for ${viewName} into ${trackedTableName}`
|
|
225
|
+
});
|
|
194
226
|
database.onChangeWithCallback(
|
|
195
227
|
{
|
|
196
228
|
onChange: async () => {
|
|
@@ -205,12 +237,12 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
205
237
|
);
|
|
206
238
|
await afterOnChangeRegistered?.();
|
|
207
239
|
if (abortController.signal.aborted) {
|
|
208
|
-
await
|
|
240
|
+
await disposeTrackingAfterAbort();
|
|
209
241
|
} else {
|
|
210
242
|
abortController.signal.addEventListener(
|
|
211
243
|
`abort`,
|
|
212
|
-
|
|
213
|
-
|
|
244
|
+
() => {
|
|
245
|
+
void disposeTrackingAfterAbort();
|
|
214
246
|
},
|
|
215
247
|
{ once: true }
|
|
216
248
|
);
|
|
@@ -218,13 +250,13 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
218
250
|
}
|
|
219
251
|
function runEagerSync() {
|
|
220
252
|
let onUnload = null;
|
|
221
|
-
|
|
253
|
+
let onUnloadStarted = false;
|
|
254
|
+
const startup = start(async () => {
|
|
222
255
|
const cleanup = await restConfig.onLoad?.();
|
|
256
|
+
onUnload = cleanup;
|
|
223
257
|
if (abortController.signal.aborted) {
|
|
224
|
-
cleanup?.();
|
|
225
258
|
return;
|
|
226
259
|
}
|
|
227
|
-
onUnload = cleanup;
|
|
228
260
|
const appliedReceipts = [];
|
|
229
261
|
await establishTracking(
|
|
230
262
|
{
|
|
@@ -247,38 +279,77 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
247
279
|
await Promise.all(appliedReceipts);
|
|
248
280
|
markReady();
|
|
249
281
|
}).catch((error) => {
|
|
250
|
-
database.logger.
|
|
251
|
-
|
|
282
|
+
database.logger.log({
|
|
283
|
+
level: common.LogLevels.error,
|
|
284
|
+
message: `Could not start syncing process for ${viewName} into ${trackedTableName}`,
|
|
252
285
|
error
|
|
253
|
-
);
|
|
286
|
+
});
|
|
254
287
|
if (collection.status === `loading`) {
|
|
255
288
|
markError(error);
|
|
256
289
|
}
|
|
257
290
|
});
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
291
|
+
const invokeOnUnload = () => {
|
|
292
|
+
if (onUnloadStarted || !onUnload) return;
|
|
293
|
+
onUnloadStarted = true;
|
|
294
|
+
const cleanup = onUnload;
|
|
295
|
+
onUnload = null;
|
|
296
|
+
return cleanup();
|
|
297
|
+
};
|
|
298
|
+
return async () => {
|
|
299
|
+
database.logger.log({
|
|
300
|
+
level: common.LogLevels.info,
|
|
301
|
+
message: `Sync has been stopped for ${viewName} into ${trackedTableName}`
|
|
302
|
+
});
|
|
262
303
|
abortController.abort();
|
|
263
|
-
|
|
304
|
+
const trackingDisposal = disposeTrackingAfterAbort();
|
|
305
|
+
let onUnloadFailure;
|
|
306
|
+
const settleOnUnload = async () => {
|
|
307
|
+
try {
|
|
308
|
+
await invokeOnUnload();
|
|
309
|
+
} catch (error) {
|
|
310
|
+
onUnloadFailure ??= { error };
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
const settlements = await Promise.allSettled([
|
|
314
|
+
startup,
|
|
315
|
+
trackingDisposal,
|
|
316
|
+
settleOnUnload()
|
|
317
|
+
]);
|
|
318
|
+
const disposalSettlement = settlements[1];
|
|
319
|
+
await settleOnUnload();
|
|
320
|
+
if (disposalSettlement.status === `rejected` && onUnloadFailure) {
|
|
321
|
+
throw new AggregateError(
|
|
322
|
+
[disposalSettlement.reason, onUnloadFailure.error],
|
|
323
|
+
`PowerSync tracking disposal and load-hook cleanup both failed`,
|
|
324
|
+
{ cause: disposalSettlement.reason }
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
if (disposalSettlement.status === `rejected`) {
|
|
328
|
+
throw disposalSettlement.reason;
|
|
329
|
+
}
|
|
330
|
+
if (onUnloadFailure) throw onUnloadFailure.error;
|
|
264
331
|
};
|
|
265
332
|
}
|
|
266
333
|
function runOnDemandSync() {
|
|
267
334
|
const demands = /* @__PURE__ */ new Map();
|
|
268
335
|
const releasedSubsets = /* @__PURE__ */ new WeakSet();
|
|
269
336
|
const pendingReleases = [];
|
|
337
|
+
const pendingDemandLoads = /* @__PURE__ */ new Set();
|
|
338
|
+
const pendingDemandCleanups = /* @__PURE__ */ new Set();
|
|
270
339
|
let stopped = false;
|
|
271
340
|
let trackingRevision = 0;
|
|
272
341
|
let reconciledTrackingRevision = 0;
|
|
273
342
|
let rebuildPromise = null;
|
|
274
343
|
let drainingReleases = false;
|
|
275
344
|
let releaseRetryTimer;
|
|
345
|
+
let demandCleanupCollector = null;
|
|
276
346
|
const startup = start();
|
|
277
347
|
void startup.catch(
|
|
278
|
-
(error) => database.logger.
|
|
279
|
-
|
|
348
|
+
(error) => database.logger.log({
|
|
349
|
+
level: common.LogLevels.error,
|
|
350
|
+
message: `Could not start syncing process for ${viewName} into ${trackedTableName}`,
|
|
280
351
|
error
|
|
281
|
-
)
|
|
352
|
+
})
|
|
282
353
|
);
|
|
283
354
|
const activeWhereExpressions = () => Array.from(demands.values()).filter((demand) => demand.active).map((demand) => demand.options.where);
|
|
284
355
|
const reconcileTracking = async () => {
|
|
@@ -293,41 +364,52 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
293
364
|
await safelyDisposeTracking(ctx);
|
|
294
365
|
if (!isCurrent()) return;
|
|
295
366
|
const active = activeWhereExpressions();
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
367
|
+
const missing = collection.status === `error` ? new Set(collection.keys()) : void 0;
|
|
368
|
+
if (active.length > 0) {
|
|
369
|
+
const combinedWhere = active.length === 1 ? active[0] : db.or(active[0], active[1], ...active.slice(2));
|
|
370
|
+
const compiledNewData = sqliteCompiler.compileSQLite(
|
|
371
|
+
{ where: combinedWhere },
|
|
372
|
+
{ jsonColumn: "NEW.data" }
|
|
373
|
+
);
|
|
374
|
+
const compiledOldData = sqliteCompiler.compileSQLite(
|
|
375
|
+
{ where: combinedWhere },
|
|
376
|
+
{ jsonColumn: "OLD.data" }
|
|
377
|
+
);
|
|
378
|
+
const compiledView = sqliteCompiler.compileSQLite({ where: combinedWhere });
|
|
379
|
+
const newDataWhenClause = toInlinedWhereClause(compiledNewData);
|
|
380
|
+
const oldDataWhenClause = toInlinedWhereClause(compiledOldData);
|
|
381
|
+
const viewWhereClause = toInlinedWhereClause(compiledView);
|
|
382
|
+
await establishTracking(
|
|
383
|
+
{
|
|
384
|
+
setupContext: ctx,
|
|
385
|
+
when: {
|
|
386
|
+
[common.DiffTriggerOperation.INSERT]: newDataWhenClause,
|
|
387
|
+
[common.DiffTriggerOperation.UPDATE]: `(${newDataWhenClause}) OR (${oldDataWhenClause})`,
|
|
388
|
+
[common.DiffTriggerOperation.DELETE]: oldDataWhenClause
|
|
389
|
+
},
|
|
390
|
+
writeType: (rowId) => collection.has(rowId) ? `update` : `insert`,
|
|
391
|
+
batchQuery: (lockContext, batchSize, cursor) => lockContext.getAll(
|
|
392
|
+
`SELECT * FROM ${viewName} WHERE ${viewWhereClause} LIMIT ? OFFSET ?`,
|
|
393
|
+
[batchSize, cursor]
|
|
394
|
+
).then((rows) => {
|
|
395
|
+
for (const row of rows) missing?.delete(row.id);
|
|
396
|
+
return rows;
|
|
397
|
+
})
|
|
317
398
|
},
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
[batchSize, cursor]
|
|
322
|
-
)
|
|
323
|
-
},
|
|
324
|
-
appliedReceipts
|
|
325
|
-
);
|
|
399
|
+
appliedReceipts
|
|
400
|
+
);
|
|
401
|
+
}
|
|
326
402
|
if (!isCurrent()) await safelyDisposeTracking(ctx);
|
|
403
|
+
else if (missing?.size) {
|
|
404
|
+
begin();
|
|
405
|
+
for (const key of missing) write({ type: `delete`, key });
|
|
406
|
+
appliedReceipts.push(commit());
|
|
407
|
+
}
|
|
327
408
|
});
|
|
328
409
|
await Promise.all(appliedReceipts);
|
|
329
410
|
if (isCurrent()) {
|
|
330
411
|
reconciledTrackingRevision = revision;
|
|
412
|
+
if (collection.status === `error`) markReady();
|
|
331
413
|
}
|
|
332
414
|
}
|
|
333
415
|
};
|
|
@@ -358,6 +440,13 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
358
440
|
if (cleanup) demand.cleanup = cleanup;
|
|
359
441
|
} catch (error) {
|
|
360
442
|
demands.delete(options);
|
|
443
|
+
const collector = demandCleanupCollector;
|
|
444
|
+
if (collector) {
|
|
445
|
+
collector.failures.push({
|
|
446
|
+
order: collector.nextOrder++,
|
|
447
|
+
error
|
|
448
|
+
});
|
|
449
|
+
}
|
|
361
450
|
throw error;
|
|
362
451
|
}
|
|
363
452
|
if (
|
|
@@ -365,14 +454,20 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
365
454
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
366
455
|
stopped || releasedSubsets.has(options) || options.signal?.aborted || demands.get(options) !== demand
|
|
367
456
|
) {
|
|
368
|
-
|
|
369
|
-
demand.cleanup?.();
|
|
457
|
+
cleanupDemand(demand);
|
|
370
458
|
return;
|
|
371
459
|
}
|
|
372
460
|
demand.active = true;
|
|
373
461
|
trackingRevision++;
|
|
374
462
|
await rebuildTracking();
|
|
375
463
|
};
|
|
464
|
+
const trackLoadSubset = (options) => {
|
|
465
|
+
const task = loadSubset(options);
|
|
466
|
+
pendingDemandLoads.add(task);
|
|
467
|
+
const finish = () => pendingDemandLoads.delete(task);
|
|
468
|
+
void task.then(finish, finish);
|
|
469
|
+
return task;
|
|
470
|
+
};
|
|
376
471
|
const toInlinedWhereClause = (compiled) => {
|
|
377
472
|
if (!compiled.where) return "TRUE";
|
|
378
473
|
const sqlParts = compiled.where.split("?");
|
|
@@ -381,15 +476,55 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
381
476
|
...compiled.params
|
|
382
477
|
);
|
|
383
478
|
};
|
|
479
|
+
const reportDemandCleanupFailure = (error) => {
|
|
480
|
+
database.logger.log({
|
|
481
|
+
level: common.LogLevels.error,
|
|
482
|
+
message: `Could not clean up subset hook for ${viewName}`,
|
|
483
|
+
error
|
|
484
|
+
});
|
|
485
|
+
};
|
|
384
486
|
const cleanupDemand = (demand) => {
|
|
385
487
|
demands.delete(demand.options);
|
|
488
|
+
const collector = demandCleanupCollector;
|
|
489
|
+
const order = collector ? collector.nextOrder++ : void 0;
|
|
386
490
|
try {
|
|
387
|
-
demand.cleanup
|
|
491
|
+
const cleanup = demand.cleanup;
|
|
492
|
+
demand.cleanup = void 0;
|
|
493
|
+
const result = cleanup?.();
|
|
494
|
+
if (!isPromiseLike(result)) return;
|
|
495
|
+
const task = Promise.resolve(result);
|
|
496
|
+
pendingDemandCleanups.add(task);
|
|
497
|
+
const finish = () => pendingDemandCleanups.delete(task);
|
|
498
|
+
void task.then(finish, finish);
|
|
499
|
+
if (collector) {
|
|
500
|
+
collector.tasks.push({ order, promise: task });
|
|
501
|
+
collector.ownedTasks.add(task);
|
|
502
|
+
void task.catch(() => void 0);
|
|
503
|
+
} else {
|
|
504
|
+
void task.catch((error) => {
|
|
505
|
+
if (!demandCleanupCollector?.ownedTasks.has(task)) {
|
|
506
|
+
reportDemandCleanupFailure(error);
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
}
|
|
388
510
|
} catch (error) {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
511
|
+
if (collector) {
|
|
512
|
+
collector.failures.push({ order, error });
|
|
513
|
+
} else {
|
|
514
|
+
reportDemandCleanupFailure(error);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
const throwDemandCleanupFailures = (collector, primaryFailures = []) => {
|
|
519
|
+
const errors = [
|
|
520
|
+
...primaryFailures,
|
|
521
|
+
...collector.failures.slice().sort((left, right) => left.order - right.order).map(({ error }) => error)
|
|
522
|
+
];
|
|
523
|
+
if (errors.length === 1) throw errors[0];
|
|
524
|
+
if (errors.length > 1) {
|
|
525
|
+
throw new AggregateError(errors, `PowerSync cleanup failed`, {
|
|
526
|
+
cause: errors[0]
|
|
527
|
+
});
|
|
393
528
|
}
|
|
394
529
|
};
|
|
395
530
|
const performPhysicalRelease = async (options) => {
|
|
@@ -453,10 +588,11 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
453
588
|
3e4
|
|
454
589
|
);
|
|
455
590
|
retryDelay = retryDelay === 0 ? delay : Math.min(retryDelay, delay);
|
|
456
|
-
database.logger.
|
|
457
|
-
|
|
591
|
+
database.logger.log({
|
|
592
|
+
level: common.LogLevels.error,
|
|
593
|
+
message: `Could not release subset tracking for ${viewName}; retrying`,
|
|
458
594
|
error
|
|
459
|
-
);
|
|
595
|
+
});
|
|
460
596
|
}
|
|
461
597
|
}
|
|
462
598
|
} finally {
|
|
@@ -484,16 +620,58 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
484
620
|
stopped = true;
|
|
485
621
|
clearTimeout(releaseRetryTimer);
|
|
486
622
|
releaseRetryTimer = void 0;
|
|
487
|
-
database.logger.
|
|
488
|
-
|
|
489
|
-
|
|
623
|
+
database.logger.log({
|
|
624
|
+
level: common.LogLevels.info,
|
|
625
|
+
message: `Sync has been stopped for ${viewName} into ${trackedTableName}`
|
|
626
|
+
});
|
|
490
627
|
abortController.abort();
|
|
628
|
+
const trackingDisposal = disposeTrackingAfterAbort();
|
|
629
|
+
const pendingLoads = [...pendingDemandLoads];
|
|
630
|
+
const pendingTasks = [...pendingDemandCleanups];
|
|
631
|
+
const collector = {
|
|
632
|
+
tasks: pendingTasks.map((promise, order) => ({ order, promise })),
|
|
633
|
+
ownedTasks: new Set(pendingTasks),
|
|
634
|
+
failures: [],
|
|
635
|
+
nextOrder: pendingTasks.length
|
|
636
|
+
};
|
|
637
|
+
demandCleanupCollector = collector;
|
|
491
638
|
for (const demand of demands.values()) {
|
|
492
639
|
cleanupDemand(demand);
|
|
493
640
|
}
|
|
494
641
|
pendingReleases.length = 0;
|
|
642
|
+
return (async () => {
|
|
643
|
+
const ownershipSettlements = await Promise.allSettled([
|
|
644
|
+
trackingDisposal,
|
|
645
|
+
...pendingLoads
|
|
646
|
+
]);
|
|
647
|
+
const disposalSettlement = ownershipSettlements[0];
|
|
648
|
+
let settledTasks = 0;
|
|
649
|
+
while (settledTasks < collector.tasks.length) {
|
|
650
|
+
const tasks = collector.tasks.slice(settledTasks);
|
|
651
|
+
settledTasks = collector.tasks.length;
|
|
652
|
+
const outcomes = await Promise.allSettled(
|
|
653
|
+
tasks.map(({ promise }) => promise)
|
|
654
|
+
);
|
|
655
|
+
for (const [index, outcome] of outcomes.entries()) {
|
|
656
|
+
if (outcome.status === `rejected`) {
|
|
657
|
+
collector.failures.push({
|
|
658
|
+
order: tasks[index].order,
|
|
659
|
+
error: outcome.reason
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
throwDemandCleanupFailures(
|
|
665
|
+
collector,
|
|
666
|
+
disposalSettlement.status === `rejected` ? [disposalSettlement.reason] : []
|
|
667
|
+
);
|
|
668
|
+
})().finally(() => {
|
|
669
|
+
if (demandCleanupCollector === collector) {
|
|
670
|
+
demandCleanupCollector = null;
|
|
671
|
+
}
|
|
672
|
+
});
|
|
495
673
|
},
|
|
496
|
-
loadSubset:
|
|
674
|
+
loadSubset: trackLoadSubset,
|
|
497
675
|
unloadSubset
|
|
498
676
|
};
|
|
499
677
|
}
|
|
@@ -524,13 +702,7 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
524
702
|
tableName: viewName,
|
|
525
703
|
trackedTableName,
|
|
526
704
|
metadataIsTracked,
|
|
527
|
-
serializeValue: (value) => serialization.serializeForSQLite(
|
|
528
|
-
value,
|
|
529
|
-
// This is required by the input generic
|
|
530
|
-
table,
|
|
531
|
-
// Coerce serializer to the shape that corresponds to the Table constructed from OutputType
|
|
532
|
-
serializer
|
|
533
|
-
)
|
|
705
|
+
serializeValue: (value) => serialization.serializeForSQLite(value, table, serializer)
|
|
534
706
|
})
|
|
535
707
|
}
|
|
536
708
|
};
|