statewire 0.18.1 → 0.18.2
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/client/session.d.ts +1 -1
- package/dist/client/session.d.ts.map +1 -1
- package/dist/client/session.js +48 -188
- package/dist/client/session.js.map +1 -1
- package/dist/host/http.d.ts.map +1 -1
- package/dist/host/http.js +2 -3
- package/dist/host/http.js.map +1 -1
- package/dist/host/socket.d.ts +4 -5
- package/dist/host/socket.d.ts.map +1 -1
- package/dist/host/socket.js +33 -44
- package/dist/host/socket.js.map +1 -1
- package/dist/host-internal.d.ts +0 -4
- package/dist/host-internal.d.ts.map +1 -1
- package/dist/host.d.ts +1 -1
- package/dist/statewire.d.ts +1 -11
- package/dist/statewire.d.ts.map +1 -1
- package/dist/statewire.js.map +1 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +6 -55
- package/dist/storage.js.map +1 -1
- package/dist/transports/sse.d.ts +1 -1
- package/dist/transports/sse.js.map +1 -1
- package/dist/transports/testing.js +2 -1
- package/dist/transports/testing.js.map +1 -1
- package/package.json +1 -1
- package/src/client/session.ts +67 -211
- package/src/host/http.ts +2 -9
- package/src/host/socket.ts +38 -46
- package/src/host-internal.ts +0 -4
- package/src/host.ts +1 -1
- package/src/statewire.ts +1 -8
- package/src/storage.ts +8 -92
- package/src/transports/sse.ts +1 -1
- package/src/transports/testing.ts +2 -1
package/src/client/session.ts
CHANGED
|
@@ -117,7 +117,7 @@ type Session = {
|
|
|
117
117
|
idle: boolean;
|
|
118
118
|
onSessionLost: StatewireClient.OnSessionLost | undefined;
|
|
119
119
|
onDone: (() => void) | undefined;
|
|
120
|
-
|
|
120
|
+
ctxNextSeq: number;
|
|
121
121
|
ctxPending: CtxPending[];
|
|
122
122
|
lanesOnAttach: boolean;
|
|
123
123
|
attachLaneSeqs: readonly [ReadonlySet<number>, ReadonlySet<number>];
|
|
@@ -244,20 +244,6 @@ const transact = (s: Session, fn: () => void): Promise<void> => {
|
|
|
244
244
|
adoptReset(s, stored);
|
|
245
245
|
} else if (stored !== null) {
|
|
246
246
|
s.record = stored;
|
|
247
|
-
if (stored.context !== undefined) {
|
|
248
|
-
const posted = new Set(
|
|
249
|
-
s.ctxPending.filter((p) => p.posted).map((p) => p.seq),
|
|
250
|
-
);
|
|
251
|
-
s.ctxOps = stored.context.ops;
|
|
252
|
-
s.contextDocuments = applyDocumentOps(
|
|
253
|
-
{ mode: "mounted", documents: new Map() },
|
|
254
|
-
stored.context.ops,
|
|
255
|
-
);
|
|
256
|
-
s.ctxPending = stored.context.pending.map((p) => ({
|
|
257
|
-
...p,
|
|
258
|
-
posted: posted.has(p.seq),
|
|
259
|
-
}));
|
|
260
|
-
}
|
|
261
247
|
}
|
|
262
248
|
fn();
|
|
263
249
|
const persistence = s.store.flush?.();
|
|
@@ -388,14 +374,6 @@ const settleAll = (s: Session, detach: "unmount" | "gone") => {
|
|
|
388
374
|
}
|
|
389
375
|
};
|
|
390
376
|
|
|
391
|
-
const restateContext = (s: Session) => {
|
|
392
|
-
s.ctxPending = [];
|
|
393
|
-
if (s.ctxOps === undefined) return;
|
|
394
|
-
const seq = s.record.ctxNextSeq;
|
|
395
|
-
setRecord(s, { ...s.record, ctxNextSeq: seq + 1 });
|
|
396
|
-
s.ctxPending.push({ seq, ops: s.ctxOps, posted: false });
|
|
397
|
-
};
|
|
398
|
-
|
|
399
377
|
const settleLost = (s: Session, entries: Pending[]) => {
|
|
400
378
|
for (const entry of entries) {
|
|
401
379
|
s.lifecycle?.onCovered?.(entry.inv);
|
|
@@ -432,16 +410,13 @@ const rebaseRecord = (s: Session, adopt: () => void) => {
|
|
|
432
410
|
s.lifecycle?.onCovered?.(old.inv);
|
|
433
411
|
enqueueInner(s, old.command, old.inv.params, old.resolve, old.reject);
|
|
434
412
|
}
|
|
435
|
-
restateContext(s);
|
|
436
413
|
publish(s);
|
|
437
414
|
wake(s);
|
|
438
415
|
void pump(s);
|
|
439
416
|
};
|
|
440
417
|
|
|
441
418
|
const resetRecord = (s: Session, clientId: string) =>
|
|
442
|
-
rebaseRecord(s, () =>
|
|
443
|
-
setRecord(s, { clientId, nextSeq: 1, ctxNextSeq: 1, commands: [] }),
|
|
444
|
-
);
|
|
419
|
+
rebaseRecord(s, () => setRecord(s, { clientId, nextSeq: 1, commands: [] }));
|
|
445
420
|
|
|
446
421
|
// Another sharer replaced the record's identity: their reset settles our
|
|
447
422
|
// attempted history and our survivors re-stamp on the stored clocks.
|
|
@@ -605,54 +580,11 @@ const publishIngested = (s: Session) => {
|
|
|
605
580
|
wake(s);
|
|
606
581
|
};
|
|
607
582
|
|
|
608
|
-
//
|
|
609
|
-
//
|
|
610
|
-
// absorption where the record survived, a gap refusal where it did not.
|
|
611
|
-
const snapCtxClock = (s: Session, ctxWm: number) => {
|
|
612
|
-
if (s.record.context?.recover === "version-1") {
|
|
613
|
-
const document = s.contextDocuments.documents.get(0);
|
|
614
|
-
if (s.contextDocuments.documents.size !== 1 || !document?.main)
|
|
615
|
-
throw new Error(
|
|
616
|
-
"statewire: version-1 context recovery requires one main document at doc 0",
|
|
617
|
-
);
|
|
618
|
-
const encoded = encodeDocument(document.value);
|
|
619
|
-
const ops = [
|
|
620
|
-
{
|
|
621
|
-
...(ctxWm === 0
|
|
622
|
-
? { op: "mount", protocol: document.protocol, main: true }
|
|
623
|
-
: { op: "replace", path: [] }),
|
|
624
|
-
...encoded,
|
|
625
|
-
},
|
|
626
|
-
];
|
|
627
|
-
s.ctxPending = [{ seq: ctxWm + 1, ops, posted: false }];
|
|
628
|
-
setRecord(s, {
|
|
629
|
-
...s.record,
|
|
630
|
-
ctxNextSeq: ctxWm + 2,
|
|
631
|
-
context: {
|
|
632
|
-
ops: contextSnapshot(s.contextDocuments),
|
|
633
|
-
pending: s.ctxPending,
|
|
634
|
-
},
|
|
635
|
-
});
|
|
636
|
-
return;
|
|
637
|
-
}
|
|
638
|
-
const ctxAligned =
|
|
639
|
-
s.ctxPending.every((c, i) => c.seq === ctxWm + 1 + i) &&
|
|
640
|
-
s.record.ctxNextSeq === ctxWm + s.ctxPending.length + 1;
|
|
641
|
-
if (!ctxAligned) {
|
|
642
|
-
if (ctxWm > 0)
|
|
643
|
-
throw new Error(
|
|
644
|
-
"statewire: context history is missing; use a new client id",
|
|
645
|
-
);
|
|
646
|
-
setRecord(s, { ...s.record, ctxNextSeq: ctxWm + 1 });
|
|
647
|
-
restateContext(s);
|
|
648
|
-
}
|
|
649
|
-
};
|
|
650
|
-
|
|
651
|
-
// The refusal's watermarks are exact: every held statement above them was
|
|
652
|
-
// never admitted by the live record, and the first unposted seq being beyond
|
|
583
|
+
// The refusal's watermark is exact: every held statement above it was never
|
|
584
|
+
// admitted by the live record, and the first unposted seq being beyond
|
|
653
585
|
// watermark+1 proves the record that admitted the missing prefix is gone —
|
|
654
|
-
// its history settles lost and the
|
|
655
|
-
const sessionLostInner = (s: Session, seqWm: number
|
|
586
|
+
// its history settles lost and the cmd clock restarts at the watermark.
|
|
587
|
+
const sessionLostInner = (s: Session, seqWm: number) => {
|
|
656
588
|
const held = [...s.pending];
|
|
657
589
|
settleLost(s, held);
|
|
658
590
|
s.pending = [];
|
|
@@ -660,11 +592,9 @@ const sessionLostInner = (s: Session, seqWm: number, ctxWm: number) => {
|
|
|
660
592
|
setRecord(s, {
|
|
661
593
|
clientId: s.record.clientId,
|
|
662
594
|
nextSeq: seqWm + 1,
|
|
663
|
-
ctxNextSeq: s.record.ctxNextSeq,
|
|
664
595
|
commands: [],
|
|
665
596
|
...(s.record.failed !== undefined && { failed: s.record.failed }),
|
|
666
597
|
});
|
|
667
|
-
snapCtxClock(s, ctxWm);
|
|
668
598
|
if (
|
|
669
599
|
held.length > 0 &&
|
|
670
600
|
s.onSessionLost?.(held.map((p) => p.inv)) === "requeue"
|
|
@@ -690,6 +620,9 @@ const handleHello = (
|
|
|
690
620
|
if (syn.record !== undefined && syn.record !== "ephemeral") {
|
|
691
621
|
protocolError(`unknown record: ${String(syn.record)}`);
|
|
692
622
|
}
|
|
623
|
+
if (syn.ctxSeq !== undefined && syn.ctxSeq !== 0) {
|
|
624
|
+
protocolError(`hello ctxSeq must be 0, got ${syn.ctxSeq}`);
|
|
625
|
+
}
|
|
693
626
|
s.selection =
|
|
694
627
|
syn.version === undefined || syn.protocols === undefined
|
|
695
628
|
? undefined
|
|
@@ -712,11 +645,7 @@ const handleHello = (
|
|
|
712
645
|
for (const entry of s.pending) {
|
|
713
646
|
entry.posted = attachCmd.has(entry.seq);
|
|
714
647
|
}
|
|
715
|
-
const ctxWatermark = syn.ctxSeq ?? 0;
|
|
716
|
-
s.ctxPending = s.ctxPending.filter((c) => c.seq > ctxWatermark);
|
|
717
648
|
for (const c of s.ctxPending) c.posted = attachCtx.has(c.seq);
|
|
718
|
-
snapCtxClock(s, ctxWatermark);
|
|
719
|
-
if (s.ctxOps !== undefined) persistContext(s);
|
|
720
649
|
syncBacklogInner(s);
|
|
721
650
|
s.sessionKnown = true;
|
|
722
651
|
};
|
|
@@ -754,7 +683,12 @@ const handleRefusal = (s: Session, syn: StatewireSyn) => {
|
|
|
754
683
|
if (typeof ctxSeq === "number") {
|
|
755
684
|
s.ctxPending = s.ctxPending.filter((c) => c.seq > ctxSeq);
|
|
756
685
|
for (const c of s.ctxPending) c.posted = false;
|
|
757
|
-
|
|
686
|
+
const firstCtx = s.ctxPending.find((c) => !c.posted);
|
|
687
|
+
if (firstCtx !== undefined && firstCtx.seq > ctxSeq + 1) {
|
|
688
|
+
protocolError(
|
|
689
|
+
`ctx gap on a live attach: seq ${firstCtx.seq} after watermark ${ctxSeq}`,
|
|
690
|
+
);
|
|
691
|
+
}
|
|
758
692
|
}
|
|
759
693
|
if (typeof syn.retry === "number") {
|
|
760
694
|
s.resumeAt = Date.now() + syn.retry;
|
|
@@ -763,22 +697,14 @@ const handleRefusal = (s: Session, syn: StatewireSyn) => {
|
|
|
763
697
|
return;
|
|
764
698
|
}
|
|
765
699
|
const firstCmd = s.pending.find((p) => !p.posted);
|
|
766
|
-
const firstCtx = s.ctxPending.find((c) => !c.posted);
|
|
767
700
|
const cmdGap =
|
|
768
701
|
typeof seq === "number" &&
|
|
769
702
|
firstCmd !== undefined &&
|
|
770
703
|
firstCmd.seq > Math.max(seq, s.durableSeq) + 1;
|
|
771
|
-
|
|
772
|
-
typeof ctxSeq === "number" &&
|
|
773
|
-
firstCtx !== undefined &&
|
|
774
|
-
firstCtx.seq > ctxSeq + 1;
|
|
775
|
-
if (cmdGap || ctxGap) {
|
|
704
|
+
if (cmdGap) {
|
|
776
705
|
transactBackground(
|
|
777
706
|
s,
|
|
778
|
-
() =>
|
|
779
|
-
if (cmdGap) sessionLostInner(s, seq!, ctxSeq ?? 0);
|
|
780
|
-
else snapCtxClock(s, ctxSeq!);
|
|
781
|
-
},
|
|
707
|
+
() => sessionLostInner(s, seq),
|
|
782
708
|
() => {
|
|
783
709
|
wake(s);
|
|
784
710
|
void pump(s);
|
|
@@ -814,16 +740,13 @@ type Lanes = {
|
|
|
814
740
|
|
|
815
741
|
// A lane must be strictly consecutive within a frame: the first unposted run
|
|
816
742
|
// per lane rides now, later runs ride follow-up frames.
|
|
817
|
-
const buildLanes = (
|
|
743
|
+
const buildLanes = (
|
|
744
|
+
s: Session,
|
|
745
|
+
cmdGate: "persisted" | "all" | "none" = "all",
|
|
746
|
+
): Lanes | null => {
|
|
818
747
|
const ctxEntries: CtxPending[] = [];
|
|
819
748
|
for (const entry of s.ctxPending) {
|
|
820
749
|
if (entry.posted) continue;
|
|
821
|
-
if (
|
|
822
|
-
persisted &&
|
|
823
|
-
(s.persistedRecord?.clientId !== s.record.clientId ||
|
|
824
|
-
entry.seq >= s.persistedRecord.ctxNextSeq)
|
|
825
|
-
)
|
|
826
|
-
break;
|
|
827
750
|
if (ctxEntries.length >= MAX_COMMANDS_PER_FRAME) break;
|
|
828
751
|
if (ctxEntries.length > 0 && entry.seq !== ctxEntries.at(-1)!.seq + 1)
|
|
829
752
|
break;
|
|
@@ -831,9 +754,10 @@ const buildLanes = (s: Session, persisted = false): Lanes | null => {
|
|
|
831
754
|
}
|
|
832
755
|
const cmdEntries: Pending[] = [];
|
|
833
756
|
for (const entry of s.pending) {
|
|
757
|
+
if (cmdGate === "none") break;
|
|
834
758
|
if (entry.posted) continue;
|
|
835
759
|
if (
|
|
836
|
-
persisted &&
|
|
760
|
+
cmdGate === "persisted" &&
|
|
837
761
|
(s.persistedRecord?.clientId !== s.record.clientId ||
|
|
838
762
|
entry.seq >= s.persistedRecord.nextSeq)
|
|
839
763
|
)
|
|
@@ -861,13 +785,20 @@ const buildLanes = (s: Session, persisted = false): Lanes | null => {
|
|
|
861
785
|
};
|
|
862
786
|
};
|
|
863
787
|
|
|
788
|
+
// Context is attach scoped: every attach starts at ctx seq 0 with an empty
|
|
789
|
+
// table, so the full context restates at seq 1 on the attach frame.
|
|
790
|
+
const resetContextLane = (s: Session) => {
|
|
791
|
+
const ops = contextSnapshot(s.contextDocuments);
|
|
792
|
+
s.ctxPending = ops.length === 0 ? [] : [{ seq: 1, ops, posted: false }];
|
|
793
|
+
s.ctxNextSeq = s.ctxPending.length + 1;
|
|
794
|
+
};
|
|
795
|
+
|
|
864
796
|
const takeAttachLanes = (s: Session): StatewireFrame | null => {
|
|
865
797
|
s.attachLaneSeqs = [new Set(), new Set()];
|
|
798
|
+
resetContextLane(s);
|
|
866
799
|
const wanted = s.lanesOnAttach;
|
|
867
800
|
s.lanesOnAttach = false;
|
|
868
|
-
|
|
869
|
-
return null;
|
|
870
|
-
const lanes = buildLanes(s, true);
|
|
801
|
+
const lanes = buildLanes(s, wanted && s.sessionKnown ? "persisted" : "none");
|
|
871
802
|
if (!lanes) return null;
|
|
872
803
|
s.attachLaneSeqs = [
|
|
873
804
|
new Set(lanes.cmdEntries.map((p) => p.seq)),
|
|
@@ -1299,83 +1230,38 @@ const contextSnapshot = (documents: StatewireDocuments.State) =>
|
|
|
1299
1230
|
};
|
|
1300
1231
|
});
|
|
1301
1232
|
|
|
1302
|
-
const persistContext = (s: Session) => {
|
|
1303
|
-
const ops = contextSnapshot(s.contextDocuments);
|
|
1304
|
-
s.ctxOps = ops;
|
|
1305
|
-
setRecord(s, {
|
|
1306
|
-
...s.record,
|
|
1307
|
-
context: {
|
|
1308
|
-
ops,
|
|
1309
|
-
...(s.record.context?.recover === undefined
|
|
1310
|
-
? {}
|
|
1311
|
-
: { recover: s.record.context.recover }),
|
|
1312
|
-
pending: s.ctxPending.map(({ seq, ops }) => ({ seq, ops })),
|
|
1313
|
-
},
|
|
1314
|
-
});
|
|
1315
|
-
};
|
|
1316
|
-
|
|
1317
1233
|
const changeContext = (
|
|
1318
1234
|
s: Session,
|
|
1319
1235
|
build: (documents: StatewireDocuments.State) => readonly unknown[],
|
|
1320
1236
|
): void => {
|
|
1321
|
-
if (!s.hydrated) {
|
|
1322
|
-
void s
|
|
1323
|
-
.ready!.then(() => changeContext(s, build))
|
|
1324
|
-
.catch((cause: unknown) => {
|
|
1325
|
-
const error = wrapStorageError(cause);
|
|
1326
|
-
s.storageError = error;
|
|
1327
|
-
s.transport?.storageFailed(error);
|
|
1328
|
-
});
|
|
1329
|
-
return;
|
|
1330
|
-
}
|
|
1331
1237
|
if (!s.running || isFinished(s)) return;
|
|
1332
|
-
const
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
const value = op as Record<string, unknown>;
|
|
1360
|
-
if (value["doc"] !== 0) return value;
|
|
1361
|
-
const { doc: _doc, ...rest } = value;
|
|
1362
|
-
return rest;
|
|
1363
|
-
}),
|
|
1364
|
-
posted: false,
|
|
1365
|
-
},
|
|
1366
|
-
];
|
|
1367
|
-
s.record = { ...s.record, ctxNextSeq: seq + 1 };
|
|
1368
|
-
persistContext(s);
|
|
1369
|
-
};
|
|
1370
|
-
if (!s.shared) {
|
|
1371
|
-
submit();
|
|
1372
|
-
wake(s);
|
|
1373
|
-
void pump(s);
|
|
1374
|
-
} else
|
|
1375
|
-
transactBackground(s, submit, () => {
|
|
1376
|
-
wake(s);
|
|
1377
|
-
void pump(s);
|
|
1378
|
-
});
|
|
1238
|
+
const operations = build(s.contextDocuments);
|
|
1239
|
+
if (!operations.length)
|
|
1240
|
+
throw new Error("statewire: context operations must not be empty");
|
|
1241
|
+
const offers = protocolOffers(s.options);
|
|
1242
|
+
const protocols =
|
|
1243
|
+
s.selection?.protocols ?? (offers === "*" ? undefined : offers);
|
|
1244
|
+
s.contextDocuments = applyDocumentOps(
|
|
1245
|
+
s.contextDocuments,
|
|
1246
|
+
operations,
|
|
1247
|
+
protocols,
|
|
1248
|
+
true,
|
|
1249
|
+
);
|
|
1250
|
+
s.ctxPending = [
|
|
1251
|
+
...s.ctxPending,
|
|
1252
|
+
{
|
|
1253
|
+
seq: s.ctxNextSeq++,
|
|
1254
|
+
ops: operations.map((op) => {
|
|
1255
|
+
const value = op as Record<string, unknown>;
|
|
1256
|
+
if (value["doc"] !== 0) return value;
|
|
1257
|
+
const { doc: _doc, ...rest } = value;
|
|
1258
|
+
return rest;
|
|
1259
|
+
}),
|
|
1260
|
+
posted: false,
|
|
1261
|
+
},
|
|
1262
|
+
];
|
|
1263
|
+
wake(s);
|
|
1264
|
+
void pump(s);
|
|
1379
1265
|
};
|
|
1380
1266
|
|
|
1381
1267
|
export const updateContext = (s: Session, operations: readonly unknown[]) =>
|
|
@@ -1506,11 +1392,10 @@ const loadSessionRecord = (
|
|
|
1506
1392
|
const record = stored ?? {
|
|
1507
1393
|
clientId: newClientId(),
|
|
1508
1394
|
nextSeq: 1,
|
|
1509
|
-
ctxNextSeq: 1,
|
|
1510
1395
|
commands: [],
|
|
1511
1396
|
};
|
|
1512
1397
|
if (stored === null) store.save(record);
|
|
1513
|
-
return
|
|
1398
|
+
return record;
|
|
1514
1399
|
};
|
|
1515
1400
|
|
|
1516
1401
|
export const useStatewireSession = (
|
|
@@ -1531,28 +1416,14 @@ export const useStatewireSession = (
|
|
|
1531
1416
|
);
|
|
1532
1417
|
}
|
|
1533
1418
|
const [s] = useState((): Session => {
|
|
1534
|
-
const
|
|
1419
|
+
const record =
|
|
1535
1420
|
store.ready === undefined
|
|
1536
1421
|
? loadSessionRecord(store, options)
|
|
1537
|
-
: {
|
|
1538
|
-
record: {
|
|
1539
|
-
clientId: newClientId(),
|
|
1540
|
-
nextSeq: 1,
|
|
1541
|
-
ctxNextSeq: 1,
|
|
1542
|
-
commands: [],
|
|
1543
|
-
},
|
|
1544
|
-
stored: null,
|
|
1545
|
-
};
|
|
1422
|
+
: { clientId: newClientId(), nextSeq: 1, commands: [] };
|
|
1546
1423
|
const session: Session = {
|
|
1547
1424
|
options,
|
|
1548
1425
|
selection: undefined,
|
|
1549
|
-
contextDocuments:
|
|
1550
|
-
stored?.context === undefined
|
|
1551
|
-
? { mode: "mounted", documents: new Map() }
|
|
1552
|
-
: applyDocumentOps(
|
|
1553
|
-
{ mode: "mounted", documents: new Map() },
|
|
1554
|
-
stored.context.ops,
|
|
1555
|
-
),
|
|
1426
|
+
contextDocuments: { mode: "mounted", documents: new Map() },
|
|
1556
1427
|
record,
|
|
1557
1428
|
ctx: {
|
|
1558
1429
|
beforeAttach: () => persistBeforeSend(session),
|
|
@@ -1587,9 +1458,8 @@ export const useStatewireSession = (
|
|
|
1587
1458
|
idle: false,
|
|
1588
1459
|
onSessionLost: undefined,
|
|
1589
1460
|
onDone: undefined,
|
|
1590
|
-
|
|
1591
|
-
ctxPending:
|
|
1592
|
-
stored?.context?.pending.map((p) => ({ ...p, posted: false })) ?? [],
|
|
1461
|
+
ctxNextSeq: 1,
|
|
1462
|
+
ctxPending: [],
|
|
1593
1463
|
lanesOnAttach: false,
|
|
1594
1464
|
attachLaneSeqs: [new Set(), new Set()],
|
|
1595
1465
|
frameFailures: 0,
|
|
@@ -1624,21 +1494,7 @@ export const useStatewireSession = (
|
|
|
1624
1494
|
};
|
|
1625
1495
|
if (store.ready !== undefined) {
|
|
1626
1496
|
session.ready = store.ready.then(() => {
|
|
1627
|
-
|
|
1628
|
-
session.record = record;
|
|
1629
|
-
session.contextDocuments =
|
|
1630
|
-
record.context === undefined
|
|
1631
|
-
? { mode: "mounted", documents: new Map() }
|
|
1632
|
-
: applyDocumentOps(
|
|
1633
|
-
{ mode: "mounted", documents: new Map() },
|
|
1634
|
-
record.context.ops,
|
|
1635
|
-
);
|
|
1636
|
-
session.ctxOps = record.context?.ops;
|
|
1637
|
-
session.ctxPending =
|
|
1638
|
-
record.context?.pending.map((entry) => ({
|
|
1639
|
-
...entry,
|
|
1640
|
-
posted: false,
|
|
1641
|
-
})) ?? [];
|
|
1497
|
+
session.record = loadSessionRecord(store, session.options);
|
|
1642
1498
|
session.recordPersisted = true;
|
|
1643
1499
|
session.hydrated = true;
|
|
1644
1500
|
if (session.running) session.notify();
|
package/src/host/http.ts
CHANGED
|
@@ -226,13 +226,7 @@ const admitAttachFrame = (
|
|
|
226
226
|
frame: ParsedFrame,
|
|
227
227
|
) => {
|
|
228
228
|
if (frame.ctx.length === 0 && frame.cmd.length === 0) return;
|
|
229
|
-
const admission = admitFrame(
|
|
230
|
-
h,
|
|
231
|
-
registration.rec,
|
|
232
|
-
frame,
|
|
233
|
-
registration.conn.auth,
|
|
234
|
-
registration.conn.selection,
|
|
235
|
-
);
|
|
229
|
+
const admission = admitFrame(h, registration.conn, frame);
|
|
236
230
|
if (admission.type === "refused") {
|
|
237
231
|
sendTo(h, registration.conn, JSON.stringify(admission.syn));
|
|
238
232
|
}
|
|
@@ -376,7 +370,6 @@ export const frames = async (
|
|
|
376
370
|
return Response.json({ error: "stale-lease" }, { status: 423 });
|
|
377
371
|
}
|
|
378
372
|
const clientId = leased.clientId;
|
|
379
|
-
const rec = h.clients.get(clientId)!;
|
|
380
373
|
const raw = await readFrameBody(request);
|
|
381
374
|
if (raw === null) {
|
|
382
375
|
return Response.json({ error: "frame body too large" }, { status: 413 });
|
|
@@ -395,7 +388,7 @@ export const frames = async (
|
|
|
395
388
|
}
|
|
396
389
|
let admission: ReturnType<typeof admitFrame>;
|
|
397
390
|
try {
|
|
398
|
-
admission = admitFrame(h,
|
|
391
|
+
admission = admitFrame(h, leased, frame);
|
|
399
392
|
} catch (error) {
|
|
400
393
|
if (!(error instanceof Error) || error.name !== "StatewireViolation")
|
|
401
394
|
throw error;
|