@pylonsync/sync 0.3.227 → 0.3.229
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/package.json +1 -1
- package/src/idb-warm-load.test.ts +144 -0
- package/src/index.ts +307 -63
- package/src/local-store.ts +57 -5
- package/src/multi-tab-orchestrator.ts +31 -5
- package/src/mutation-queue.ts +32 -3
- package/src/persistence.ts +69 -30
- package/src/round6-codex.test.ts +157 -0
- package/src/scenarios.test.ts +184 -0
- package/src/test-harness/server.ts +36 -0
- package/src/test-harness/transport.ts +16 -0
|
@@ -196,6 +196,42 @@ export class TestServer {
|
|
|
196
196
|
return s;
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
+
/** Inject a one-shot outcome for the NEXT push: a `network` failure
|
|
200
|
+
* (the fetch rejects with no status — simulating offline / connection
|
|
201
|
+
* reset) or an HTTP `status` (e.g. 403 permanent rejection, 503
|
|
202
|
+
* transient). Lets tests exercise the transient-vs-permanent
|
|
203
|
+
* classification in pushInner. */
|
|
204
|
+
private nextPushOutcome:
|
|
205
|
+
| { kind: "network" }
|
|
206
|
+
| { kind: "status"; status: number }
|
|
207
|
+
| null = null;
|
|
208
|
+
primeNextPushOutcome(
|
|
209
|
+
o: { kind: "network" } | { kind: "status"; status: number },
|
|
210
|
+
): void {
|
|
211
|
+
this.nextPushOutcome = o;
|
|
212
|
+
}
|
|
213
|
+
consumeNextPushOutcome():
|
|
214
|
+
| { kind: "network" }
|
|
215
|
+
| { kind: "status"; status: number }
|
|
216
|
+
| null {
|
|
217
|
+
const o = this.nextPushOutcome;
|
|
218
|
+
this.nextPushOutcome = null;
|
|
219
|
+
return o;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** Make the NEXT delta pull report `has_more: true` once, exercising
|
|
223
|
+
* the change-log tail-pull recursion in pullInner (the path that
|
|
224
|
+
* self-deadlocked before the pullInner() fix). */
|
|
225
|
+
private nextPullHasMore = false;
|
|
226
|
+
primeNextPullHasMore(): void {
|
|
227
|
+
this.nextPullHasMore = true;
|
|
228
|
+
}
|
|
229
|
+
consumeNextPullHasMore(): boolean {
|
|
230
|
+
const v = this.nextPullHasMore;
|
|
231
|
+
this.nextPullHasMore = false;
|
|
232
|
+
return v;
|
|
233
|
+
}
|
|
234
|
+
|
|
199
235
|
// ---- Entity data --------------------------------------------------------
|
|
200
236
|
|
|
201
237
|
/** Bulk-seed rows for an entity AND emit insert events into the
|
|
@@ -259,6 +259,10 @@ async function handle(
|
|
|
259
259
|
};
|
|
260
260
|
}
|
|
261
261
|
const resp = await server.pull(token, since);
|
|
262
|
+
// One-shot has_more on a delta pull → drives the tail-pull recursion.
|
|
263
|
+
if (since > 0 && server.consumeNextPullHasMore()) {
|
|
264
|
+
return { status: 200, body: { ...resp, has_more: true } };
|
|
265
|
+
}
|
|
262
266
|
return { status: 200, body: resp };
|
|
263
267
|
}
|
|
264
268
|
|
|
@@ -275,6 +279,18 @@ async function handle(
|
|
|
275
279
|
|
|
276
280
|
// /api/sync/push — accept ops from optimistic mutations.
|
|
277
281
|
if (url.endsWith("/api/sync/push") && method === "POST") {
|
|
282
|
+
const outcome = server.consumeNextPushOutcome();
|
|
283
|
+
if (outcome?.kind === "network") {
|
|
284
|
+
// Reject like a real offline fetch: no HTTP status → the engine
|
|
285
|
+
// classifies it as TRANSIENT (keep pending, retry).
|
|
286
|
+
throw new Error("simulated network failure (offline)");
|
|
287
|
+
}
|
|
288
|
+
if (outcome?.kind === "status") {
|
|
289
|
+
return {
|
|
290
|
+
status: outcome.status,
|
|
291
|
+
body: { error: { code: "PUSH_REJECTED" } },
|
|
292
|
+
};
|
|
293
|
+
}
|
|
278
294
|
return { status: 200, body: { ops: [] } };
|
|
279
295
|
}
|
|
280
296
|
|