@korajs/sync 1.0.0-beta.10 → 1.0.0-beta.11
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/index.cjs +225 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -2
- package/dist/index.d.ts +14 -2
- package/dist/index.js +224 -24
- package/dist/index.js.map +1 -1
- package/dist/internal.d.cts +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/{transport-B2GQerqJ.d.cts → transport-iXX0KA_W.d.cts} +66 -5
- package/dist/{transport-B2GQerqJ.d.ts → transport-iXX0KA_W.d.ts} +66 -5
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -86,6 +86,7 @@ __export(index_exports, {
|
|
|
86
86
|
isYjsDocUpdateMessage: () => isYjsDocUpdateMessage,
|
|
87
87
|
operationMatchesQuerySubsets: () => operationMatchesQuerySubsets,
|
|
88
88
|
operationMatchesScope: () => operationMatchesScope,
|
|
89
|
+
querySubsetContains: () => querySubsetContains,
|
|
89
90
|
richtextDocKey: () => richtextDocKey,
|
|
90
91
|
sliceOperationsAfterCursor: () => sliceOperationsAfterCursor,
|
|
91
92
|
subscribeRemoteAwarenessStates: () => subscribeRemoteAwarenessStates,
|
|
@@ -112,7 +113,8 @@ var SYNC_STATUSES = [
|
|
|
112
113
|
"offline",
|
|
113
114
|
"clock-error",
|
|
114
115
|
"error",
|
|
115
|
-
"schema-mismatch"
|
|
116
|
+
"schema-mismatch",
|
|
117
|
+
"auth-required"
|
|
116
118
|
];
|
|
117
119
|
var MemoryRejectedOperationStorage = class {
|
|
118
120
|
rejected = /* @__PURE__ */ new Map();
|
|
@@ -160,12 +162,19 @@ function operationMatchesScope(op, scopeMap, fullRecord) {
|
|
|
160
162
|
const snapshot = buildSnapshot(op, fullRecord ?? void 0);
|
|
161
163
|
if (!snapshot) return false;
|
|
162
164
|
for (const [field, expected] of Object.entries(collectionScope)) {
|
|
163
|
-
if (snapshot[field]
|
|
165
|
+
if (!matchesPredicate(snapshot[field], expected)) {
|
|
164
166
|
return false;
|
|
165
167
|
}
|
|
166
168
|
}
|
|
167
169
|
return true;
|
|
168
170
|
}
|
|
171
|
+
function matchesPredicate(actual, expected) {
|
|
172
|
+
if (expected && typeof expected === "object" && !Array.isArray(expected) && "$in" in expected) {
|
|
173
|
+
const values = expected.$in;
|
|
174
|
+
return Array.isArray(values) && values.some((value) => Object.is(actual, value));
|
|
175
|
+
}
|
|
176
|
+
return Object.is(actual, expected);
|
|
177
|
+
}
|
|
169
178
|
function filterOperationsByScope(operations, scopeMap) {
|
|
170
179
|
if (!scopeMap) return operations;
|
|
171
180
|
return operations.filter((op) => operationMatchesScope(op, scopeMap));
|
|
@@ -222,24 +231,77 @@ function operationMatchesQuerySubsets(op, subsets, fullRecord) {
|
|
|
222
231
|
if (collectionSubsets.length === 0) {
|
|
223
232
|
return true;
|
|
224
233
|
}
|
|
234
|
+
if (collectionSubsets.some((subset) => Object.keys(subset.where).length === 0)) {
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
225
237
|
const snapshot = buildSnapshot2(op, fullRecord);
|
|
226
238
|
if (!snapshot) {
|
|
227
239
|
return false;
|
|
228
240
|
}
|
|
229
241
|
return collectionSubsets.some((subset) => recordMatchesWhere(snapshot, subset.where));
|
|
230
242
|
}
|
|
243
|
+
function stableValue(value) {
|
|
244
|
+
if (Array.isArray(value)) return value.map(stableValue);
|
|
245
|
+
if (value && typeof value === "object") {
|
|
246
|
+
return Object.fromEntries(
|
|
247
|
+
Object.entries(value).filter(([, entry]) => entry !== void 0).sort(([a], [b]) => a.localeCompare(b)).map(([key, entry]) => [key, stableValue(entry)])
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
return value;
|
|
251
|
+
}
|
|
252
|
+
function whereContains(broad, narrow) {
|
|
253
|
+
return Object.entries(broad).every(
|
|
254
|
+
([field, expected]) => JSON.stringify(stableValue(narrow[field])) === JSON.stringify(stableValue(expected))
|
|
255
|
+
);
|
|
256
|
+
}
|
|
231
257
|
function dedupeQuerySubsets(subsets) {
|
|
232
|
-
const
|
|
258
|
+
const normalized = subsets.map((subset) => ({
|
|
259
|
+
collection: subset.collection,
|
|
260
|
+
where: stableValue(subset.where)
|
|
261
|
+
}));
|
|
233
262
|
const result = [];
|
|
234
|
-
for (const
|
|
235
|
-
|
|
236
|
-
|
|
263
|
+
for (const candidate of normalized) {
|
|
264
|
+
if (normalized.some(
|
|
265
|
+
(other) => other !== candidate && other.collection === candidate.collection && whereContains(other.where, candidate.where) && (Object.keys(other.where).length < Object.keys(candidate.where).length || JSON.stringify(other.where) === JSON.stringify(candidate.where))
|
|
266
|
+
)) {
|
|
267
|
+
const duplicateAlreadyKept = result.some(
|
|
268
|
+
(item) => item.collection === candidate.collection && JSON.stringify(item.where) === JSON.stringify(candidate.where)
|
|
269
|
+
);
|
|
270
|
+
if (duplicateAlreadyKept || normalized.some(
|
|
271
|
+
(other) => other !== candidate && other.collection === candidate.collection && Object.keys(other.where).length < Object.keys(candidate.where).length && whereContains(other.where, candidate.where)
|
|
272
|
+
))
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
if (!result.some(
|
|
276
|
+
(item) => item.collection === candidate.collection && JSON.stringify(item.where) === JSON.stringify(candidate.where)
|
|
277
|
+
))
|
|
278
|
+
result.push(candidate);
|
|
279
|
+
}
|
|
280
|
+
return result.sort(
|
|
281
|
+
(a, b) => `${a.collection}:${JSON.stringify(a.where)}`.localeCompare(
|
|
282
|
+
`${b.collection}:${JSON.stringify(b.where)}`
|
|
283
|
+
)
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
function querySubsetContains(broad, narrow) {
|
|
287
|
+
const collections = /* @__PURE__ */ new Set([
|
|
288
|
+
...broad.map((subset) => subset.collection),
|
|
289
|
+
...narrow.map((subset) => subset.collection)
|
|
290
|
+
]);
|
|
291
|
+
for (const collection of collections) {
|
|
292
|
+
const broadForCollection = broad.filter((subset) => subset.collection === collection);
|
|
293
|
+
const narrowForCollection = narrow.filter((subset) => subset.collection === collection);
|
|
294
|
+
if (narrowForCollection.length === 0) {
|
|
295
|
+
if (broadForCollection.length > 0) return false;
|
|
237
296
|
continue;
|
|
238
297
|
}
|
|
239
|
-
|
|
240
|
-
|
|
298
|
+
if (broadForCollection.length === 0) continue;
|
|
299
|
+
if (!narrowForCollection.every(
|
|
300
|
+
(candidate) => broadForCollection.some((other) => whereContains(other.where, candidate.where))
|
|
301
|
+
))
|
|
302
|
+
return false;
|
|
241
303
|
}
|
|
242
|
-
return
|
|
304
|
+
return true;
|
|
243
305
|
}
|
|
244
306
|
|
|
245
307
|
// src/delta/delta-cursor.ts
|
|
@@ -2818,6 +2880,11 @@ var SyncEngine = class {
|
|
|
2818
2880
|
clockBlocked = false;
|
|
2819
2881
|
clockSkewMs = null;
|
|
2820
2882
|
blobStorageEnabled = false;
|
|
2883
|
+
suspensionReason = null;
|
|
2884
|
+
authRejected = false;
|
|
2885
|
+
serverFrontier = null;
|
|
2886
|
+
hasInFlightDeliveryBatch = false;
|
|
2887
|
+
blockedFailure = null;
|
|
2821
2888
|
outboundAckTimer = null;
|
|
2822
2889
|
startPromise = null;
|
|
2823
2890
|
stopPromise = null;
|
|
@@ -2838,6 +2905,7 @@ var SyncEngine = class {
|
|
|
2838
2905
|
activeScope;
|
|
2839
2906
|
/** Live query subsets registered from reactive subscriptions */
|
|
2840
2907
|
querySubsets = /* @__PURE__ */ new Map();
|
|
2908
|
+
staticQuerySubsets = [];
|
|
2841
2909
|
querySubsetReconnectTimer = null;
|
|
2842
2910
|
/** Resume cursor for paginated initial sync (persisted across reconnects) */
|
|
2843
2911
|
resumeDeltaCursor = null;
|
|
@@ -2931,6 +2999,22 @@ var SyncEngine = class {
|
|
|
2931
2999
|
if (this.state !== "disconnected") {
|
|
2932
3000
|
await this.stop();
|
|
2933
3001
|
}
|
|
3002
|
+
if (this.authRejected) {
|
|
3003
|
+
this.emitter?.emit({
|
|
3004
|
+
type: "sync:suspended",
|
|
3005
|
+
reason: this.suspensionReason ?? "auth-rejected"
|
|
3006
|
+
});
|
|
3007
|
+
return;
|
|
3008
|
+
}
|
|
3009
|
+
if (this.config.authState) {
|
|
3010
|
+
const authState = await this.config.authState();
|
|
3011
|
+
if (authState.state === "loading" || authState.state === "signed-out") {
|
|
3012
|
+
this.suspensionReason = authState.state === "loading" ? "auth-loading" : "auth-required";
|
|
3013
|
+
this.emitter?.emit({ type: "sync:suspended", reason: this.suspensionReason });
|
|
3014
|
+
return;
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
this.suspensionReason = null;
|
|
2934
3018
|
await this.outboundQueue.initialize();
|
|
2935
3019
|
if (this.syncState) {
|
|
2936
3020
|
this.lastAckedServerVector = await this.syncState.loadLastAckedServerVector();
|
|
@@ -3060,14 +3144,30 @@ var SyncEngine = class {
|
|
|
3060
3144
|
getStatus() {
|
|
3061
3145
|
const pendingOperations = this.syncState ? this.cachedUnsyncedCount : this.outboundQueue.totalPending;
|
|
3062
3146
|
const base = {
|
|
3147
|
+
phase: this.resolvePhase(),
|
|
3148
|
+
...this.suspensionReason ? { reason: this.suspensionReason } : {},
|
|
3063
3149
|
reconnecting: this.reconnecting,
|
|
3064
3150
|
pendingOperations,
|
|
3065
3151
|
lastSyncedAt: this.lastSyncedAt,
|
|
3066
3152
|
lastSuccessfulPush: this.lastSuccessfulPush,
|
|
3067
3153
|
lastSuccessfulPull: this.lastSuccessfulPull,
|
|
3068
3154
|
conflicts: this.conflictCount,
|
|
3069
|
-
clockSkewMs: this.clockSkewMs
|
|
3155
|
+
clockSkewMs: this.clockSkewMs,
|
|
3156
|
+
inFlightUploadOperations: this.currentBatch?.operations.length ?? 0,
|
|
3157
|
+
hasInFlightDeliveryBatch: this.hasInFlightDeliveryBatch,
|
|
3158
|
+
activeViewId: this.deliverySignature(),
|
|
3159
|
+
activeViewComplete: this.state === "streaming" && !this.hasInFlightDeliveryBatch && this.blockedFailure === null && (this.serverFrontier === null || this.deliveryWatermark >= this.serverFrontier),
|
|
3160
|
+
initialSync: {
|
|
3161
|
+
complete: this.deltaReceiveComplete && this.deltaSendComplete,
|
|
3162
|
+
receivedBatches: this.deltaBatchesReceived,
|
|
3163
|
+
totalBatches: this.initialSyncTotalBatches || null,
|
|
3164
|
+
progress: this.initialSyncTotalBatches > 0 ? Math.min(1, this.deltaBatchesReceived / this.initialSyncTotalBatches) : null
|
|
3165
|
+
},
|
|
3166
|
+
deliveryWatermark: this.deliveryWatermark,
|
|
3167
|
+
serverFrontier: this.serverFrontier,
|
|
3168
|
+
blockedFailure: this.blockedFailure
|
|
3070
3169
|
};
|
|
3170
|
+
if (this.suspensionReason) return { ...base, status: "auth-required" };
|
|
3071
3171
|
switch (this.state) {
|
|
3072
3172
|
case "disconnected":
|
|
3073
3173
|
if (this.clockBlocked) {
|
|
@@ -3090,6 +3190,26 @@ var SyncEngine = class {
|
|
|
3090
3190
|
return { ...base, status: this.schemaBlocked ? "schema-mismatch" : "error" };
|
|
3091
3191
|
}
|
|
3092
3192
|
}
|
|
3193
|
+
resolvePhase() {
|
|
3194
|
+
if (this.suspensionReason) return "suspended";
|
|
3195
|
+
if (this.blockedFailure || this.schemaBlocked || this.clockBlocked) return "blocked";
|
|
3196
|
+
if (this.hasInFlightDeliveryBatch) return "applying";
|
|
3197
|
+
if (this.currentBatch) return "uploading";
|
|
3198
|
+
switch (this.state) {
|
|
3199
|
+
case "disconnected":
|
|
3200
|
+
return "offline";
|
|
3201
|
+
case "connecting":
|
|
3202
|
+
return "connecting";
|
|
3203
|
+
case "handshaking":
|
|
3204
|
+
return "handshaking";
|
|
3205
|
+
case "syncing":
|
|
3206
|
+
return "receiving";
|
|
3207
|
+
case "streaming":
|
|
3208
|
+
return "streaming";
|
|
3209
|
+
case "error":
|
|
3210
|
+
return "blocked";
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3093
3213
|
/**
|
|
3094
3214
|
* True when the server rejected the client's schema version at handshake.
|
|
3095
3215
|
* Sync stays blocked until the app schema is upgraded or sync config changes.
|
|
@@ -3142,8 +3262,25 @@ var SyncEngine = class {
|
|
|
3142
3262
|
await this.refreshPendingCount();
|
|
3143
3263
|
return this.getStatus().pendingOperations;
|
|
3144
3264
|
}
|
|
3145
|
-
emitApplyFailure(op, result, overrides) {
|
|
3265
|
+
emitApplyFailure(op, result, overrides, blocking = false) {
|
|
3146
3266
|
const reason = (0, import_core7.defaultApplyFailureReason)(result, overrides);
|
|
3267
|
+
if (blocking || reason.retriable) {
|
|
3268
|
+
const prior = this.blockedFailure;
|
|
3269
|
+
this.blockedFailure = {
|
|
3270
|
+
operationId: op.id,
|
|
3271
|
+
collection: op.collection,
|
|
3272
|
+
recordId: op.recordId,
|
|
3273
|
+
code: reason.code,
|
|
3274
|
+
message: reason.message,
|
|
3275
|
+
retriable: reason.retriable,
|
|
3276
|
+
firstSeenAt: prior?.operationId === op.id ? prior.firstSeenAt : Date.now(),
|
|
3277
|
+
retryCount: prior?.operationId === op.id ? prior.retryCount + 1 : 0
|
|
3278
|
+
};
|
|
3279
|
+
this.emitter?.emit({
|
|
3280
|
+
type: prior?.operationId === op.id ? "sync:apply-retrying" : "sync:apply-blocked",
|
|
3281
|
+
failure: this.blockedFailure
|
|
3282
|
+
});
|
|
3283
|
+
}
|
|
3147
3284
|
this.emitter?.emit({
|
|
3148
3285
|
type: "sync:apply-failed",
|
|
3149
3286
|
operationId: op.id,
|
|
@@ -3160,11 +3297,18 @@ var SyncEngine = class {
|
|
|
3160
3297
|
*/
|
|
3161
3298
|
async retryNow() {
|
|
3162
3299
|
if (this.schemaBlocked) return;
|
|
3300
|
+
this.authRejected = false;
|
|
3301
|
+
this.suspensionReason = null;
|
|
3163
3302
|
if (this.state === "disconnected" || this.state === "error") {
|
|
3164
3303
|
this.reconnecting = false;
|
|
3165
3304
|
await this.start();
|
|
3166
3305
|
}
|
|
3167
3306
|
}
|
|
3307
|
+
/** Wake a permanent auth suspension after the binding reports a new auth state. */
|
|
3308
|
+
notifyAuthChanged() {
|
|
3309
|
+
this.authRejected = false;
|
|
3310
|
+
this.suspensionReason = null;
|
|
3311
|
+
}
|
|
3168
3312
|
/**
|
|
3169
3313
|
* Atomically refresh the transport session. Overlapping callers share one
|
|
3170
3314
|
* reconnect, in-flight outbound batches are returned to the queue by stop(), and the
|
|
@@ -3321,23 +3465,49 @@ var SyncEngine = class {
|
|
|
3321
3465
|
* Takes effect on the next connection; reconnects when already connected.
|
|
3322
3466
|
*/
|
|
3323
3467
|
registerQuerySubset(subset) {
|
|
3468
|
+
if ((this.config.querySubsets?.mode ?? "reactive") !== "reactive") return () => {
|
|
3469
|
+
};
|
|
3324
3470
|
const id = `query-${nextQuerySubsetId++}`;
|
|
3325
3471
|
const previousSignature = this.deliverySignature();
|
|
3472
|
+
const previousEffective = this.getActiveQuerySubsets();
|
|
3326
3473
|
this.querySubsets.set(id, subset);
|
|
3327
|
-
this.
|
|
3328
|
-
this.scheduleQuerySubsetReconnect();
|
|
3474
|
+
this.switchDeliveryViewWithInheritance(previousSignature, previousEffective);
|
|
3475
|
+
if (this.deliverySignature() !== previousSignature) this.scheduleQuerySubsetReconnect();
|
|
3329
3476
|
return () => {
|
|
3330
3477
|
const sigBeforeRemove = this.deliverySignature();
|
|
3478
|
+
const effectiveBeforeRemove = this.getActiveQuerySubsets();
|
|
3331
3479
|
this.querySubsets.delete(id);
|
|
3332
|
-
this.
|
|
3333
|
-
this.scheduleQuerySubsetReconnect();
|
|
3480
|
+
this.switchDeliveryViewWithInheritance(sigBeforeRemove, effectiveBeforeRemove);
|
|
3481
|
+
if (this.deliverySignature() !== sigBeforeRemove) this.scheduleQuerySubsetReconnect();
|
|
3334
3482
|
};
|
|
3335
3483
|
}
|
|
3484
|
+
switchDeliveryViewWithInheritance(previousSignature, previousSubsets) {
|
|
3485
|
+
const nextSubsets = this.getActiveQuerySubsets();
|
|
3486
|
+
const previousWatermark = this.deliveryWatermark;
|
|
3487
|
+
this.switchDeliveryView(previousSignature);
|
|
3488
|
+
if (this.deliveryWatermark === 0 && querySubsetContains(previousSubsets, nextSubsets)) {
|
|
3489
|
+
this.deliveryWatermark = previousWatermark;
|
|
3490
|
+
this.setViewWatermark(this.deliverySignature(), previousWatermark);
|
|
3491
|
+
void this.persistDeliveryWatermark(previousWatermark);
|
|
3492
|
+
}
|
|
3493
|
+
}
|
|
3494
|
+
/** Atomically replace the manifest used by static query-subset mode. */
|
|
3495
|
+
setQuerySubsets(subsets) {
|
|
3496
|
+
if ((this.config.querySubsets?.mode ?? "reactive") !== "static")
|
|
3497
|
+
throw new Error('setQuerySubsets() requires sync.querySubsets.mode = "static"');
|
|
3498
|
+
const previousSignature = this.deliverySignature();
|
|
3499
|
+
const previousEffective = this.getActiveQuerySubsets();
|
|
3500
|
+
this.staticQuerySubsets = dedupeQuerySubsets(subsets);
|
|
3501
|
+
this.switchDeliveryViewWithInheritance(previousSignature, previousEffective);
|
|
3502
|
+
if (this.deliverySignature() !== previousSignature) this.scheduleQuerySubsetReconnect();
|
|
3503
|
+
}
|
|
3336
3504
|
/**
|
|
3337
3505
|
* Returns deduplicated active query subsets from registered subscriptions.
|
|
3338
3506
|
*/
|
|
3339
3507
|
getActiveQuerySubsets() {
|
|
3340
|
-
|
|
3508
|
+
const mode = this.config.querySubsets?.mode ?? "reactive";
|
|
3509
|
+
if (mode === "disabled") return [];
|
|
3510
|
+
return mode === "static" ? this.staticQuerySubsets : dedupeQuerySubsets([...this.querySubsets.values()]);
|
|
3341
3511
|
}
|
|
3342
3512
|
/**
|
|
3343
3513
|
* Get the awareness manager for collaborative presence.
|
|
@@ -3414,6 +3584,7 @@ var SyncEngine = class {
|
|
|
3414
3584
|
}
|
|
3415
3585
|
}
|
|
3416
3586
|
handleMessageFailure(error) {
|
|
3587
|
+
this.hasInFlightDeliveryBatch = false;
|
|
3417
3588
|
const reason = error instanceof Error ? error.message : "Message handling failed";
|
|
3418
3589
|
this.handleTransportClose(reason);
|
|
3419
3590
|
}
|
|
@@ -3487,6 +3658,7 @@ var SyncEngine = class {
|
|
|
3487
3658
|
this.setViewWatermark(this.deliverySignature(), 0);
|
|
3488
3659
|
await this.persistDeliveryWatermark(0);
|
|
3489
3660
|
}
|
|
3661
|
+
this.serverFrontier = msg.serverMaxDeliverySequence ?? null;
|
|
3490
3662
|
if (msg.selectedWireFormat) {
|
|
3491
3663
|
this.setSerializerWireFormat(msg.selectedWireFormat);
|
|
3492
3664
|
}
|
|
@@ -3648,6 +3820,7 @@ var SyncEngine = class {
|
|
|
3648
3820
|
this.sendDeliveryAck(msg.messageId, this.deliveryWatermark);
|
|
3649
3821
|
return;
|
|
3650
3822
|
}
|
|
3823
|
+
this.hasInFlightDeliveryBatch = true;
|
|
3651
3824
|
}
|
|
3652
3825
|
const deserialized = msg.operations.map((s) => this.serializer.decodeOperation(s));
|
|
3653
3826
|
const operations = this.encryptor ? await this.encryptor.decryptBatch(deserialized) : deserialized;
|
|
@@ -3668,17 +3841,22 @@ var SyncEngine = class {
|
|
|
3668
3841
|
} catch (error) {
|
|
3669
3842
|
fullyApplied = false;
|
|
3670
3843
|
if (error instanceof import_core7.ClockDriftError) {
|
|
3671
|
-
this.emitApplyFailure(
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3844
|
+
this.emitApplyFailure(
|
|
3845
|
+
transformed,
|
|
3846
|
+
"rejected",
|
|
3847
|
+
{
|
|
3848
|
+
code: import_core7.APPLY_FAILURE_CODES.CLOCK_DRIFT,
|
|
3849
|
+
message: error.message,
|
|
3850
|
+
retriable: false
|
|
3851
|
+
},
|
|
3852
|
+
true
|
|
3853
|
+
);
|
|
3676
3854
|
continue;
|
|
3677
3855
|
}
|
|
3678
3856
|
const message = error instanceof Error ? error.message : "Apply failed";
|
|
3679
3857
|
const code = error instanceof import_core7.SyncError ? error.code : error instanceof import_core7.KoraError ? error.code : error instanceof Error && "code" in error && typeof error.code === "string" ? error.code : import_core7.APPLY_FAILURE_CODES.APPLY_FAILED;
|
|
3680
3858
|
const retriable = code !== import_core7.APPLY_FAILURE_CODES.REFERENTIAL_INTEGRITY;
|
|
3681
|
-
this.emitApplyFailure(transformed, "rejected", { code, message, retriable });
|
|
3859
|
+
this.emitApplyFailure(transformed, "rejected", { code, message, retriable }, true);
|
|
3682
3860
|
}
|
|
3683
3861
|
}
|
|
3684
3862
|
if (inScopeOps.length > 0) {
|
|
@@ -3711,7 +3889,13 @@ var SyncEngine = class {
|
|
|
3711
3889
|
this.deliveryWatermark = msg.maxDeliverySequence;
|
|
3712
3890
|
this.setViewWatermark(this.deliverySignature(), this.deliveryWatermark);
|
|
3713
3891
|
await this.persistDeliveryWatermark(this.deliveryWatermark);
|
|
3892
|
+
if (this.blockedFailure && inScopeOps.some((op) => op.id === this.blockedFailure?.operationId)) {
|
|
3893
|
+
const recovered = this.blockedFailure;
|
|
3894
|
+
this.blockedFailure = null;
|
|
3895
|
+
this.emitter?.emit({ type: "sync:apply-recovered", failure: recovered });
|
|
3896
|
+
}
|
|
3714
3897
|
}
|
|
3898
|
+
this.hasInFlightDeliveryBatch = false;
|
|
3715
3899
|
if (this.state === "syncing") {
|
|
3716
3900
|
this.deltaBatchesReceived++;
|
|
3717
3901
|
const totalBatches = msg.totalBatches ?? this.initialSyncTotalBatches;
|
|
@@ -3784,8 +3968,11 @@ var SyncEngine = class {
|
|
|
3784
3968
|
this.currentBatchRequiresPartialAck = false;
|
|
3785
3969
|
}
|
|
3786
3970
|
this.transitionTo("error");
|
|
3787
|
-
if (msg.code === "AUTH_FAILED") {
|
|
3971
|
+
if (msg.code === "AUTH_FAILED" || msg.code === "DEVICE_REVOKED") {
|
|
3972
|
+
this.authRejected = true;
|
|
3973
|
+
this.suspensionReason = msg.code === "DEVICE_REVOKED" ? "device-revoked" : "auth-rejected";
|
|
3788
3974
|
this.emitter?.emit({ type: "sync:auth-failed", reason: msg.message });
|
|
3975
|
+
this.emitter?.emit({ type: "sync:suspended", reason: this.suspensionReason });
|
|
3789
3976
|
}
|
|
3790
3977
|
if (msg.code === "INVALID_TIMESTAMP") {
|
|
3791
3978
|
this.clockBlocked = true;
|
|
@@ -4829,13 +5016,22 @@ function fromBase642(str) {
|
|
|
4829
5016
|
// src/reactivity/sync-status-controller.ts
|
|
4830
5017
|
var OFFLINE_SYNC_STATUS = Object.freeze({
|
|
4831
5018
|
status: "offline",
|
|
5019
|
+
phase: "offline",
|
|
4832
5020
|
reconnecting: false,
|
|
4833
5021
|
pendingOperations: 0,
|
|
4834
5022
|
lastSyncedAt: null,
|
|
4835
5023
|
lastSuccessfulPush: null,
|
|
4836
5024
|
lastSuccessfulPull: null,
|
|
4837
5025
|
conflicts: 0,
|
|
4838
|
-
clockSkewMs: null
|
|
5026
|
+
clockSkewMs: null,
|
|
5027
|
+
inFlightUploadOperations: 0,
|
|
5028
|
+
hasInFlightDeliveryBatch: false,
|
|
5029
|
+
activeViewId: "",
|
|
5030
|
+
activeViewComplete: false,
|
|
5031
|
+
initialSync: { complete: false, receivedBatches: 0, totalBatches: null, progress: null },
|
|
5032
|
+
deliveryWatermark: 0,
|
|
5033
|
+
serverFrontier: null,
|
|
5034
|
+
blockedFailure: null
|
|
4839
5035
|
});
|
|
4840
5036
|
var SYNC_STATUS_EVENT_TYPES = [
|
|
4841
5037
|
"sync:connected",
|
|
@@ -4847,6 +5043,10 @@ var SYNC_STATUS_EVENT_TYPES = [
|
|
|
4847
5043
|
"sync:received",
|
|
4848
5044
|
"sync:acknowledged",
|
|
4849
5045
|
"sync:apply-failed",
|
|
5046
|
+
"sync:suspended",
|
|
5047
|
+
"sync:apply-blocked",
|
|
5048
|
+
"sync:apply-retrying",
|
|
5049
|
+
"sync:apply-recovered",
|
|
4850
5050
|
"sync:delivery-gap",
|
|
4851
5051
|
"sync:diagnostics",
|
|
4852
5052
|
"sync:initial-sync-progress"
|
|
@@ -5039,6 +5239,7 @@ function subscribeRemoteAwarenessStates(awareness, listener) {
|
|
|
5039
5239
|
isYjsDocUpdateMessage,
|
|
5040
5240
|
operationMatchesQuerySubsets,
|
|
5041
5241
|
operationMatchesScope,
|
|
5242
|
+
querySubsetContains,
|
|
5042
5243
|
richtextDocKey,
|
|
5043
5244
|
sliceOperationsAfterCursor,
|
|
5044
5245
|
subscribeRemoteAwarenessStates,
|