statewire 0.9.2 → 0.11.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.
- package/dist/client/connection.d.ts +1 -0
- package/dist/client/connection.d.ts.map +1 -1
- package/dist/client/connection.js +8 -1
- package/dist/client/connection.js.map +1 -1
- package/dist/client/session.d.ts +13 -4
- package/dist/client/session.d.ts.map +1 -1
- package/dist/client/session.js +263 -66
- package/dist/client/session.js.map +1 -1
- package/dist/client/sessionStore.d.ts +5 -14
- package/dist/client/sessionStore.d.ts.map +1 -1
- package/dist/client/sessionStore.js +4 -1
- package/dist/client/sessionStore.js.map +1 -1
- package/dist/host/http.d.ts +7 -0
- package/dist/host/http.d.ts.map +1 -0
- package/dist/host/http.js +175 -0
- package/dist/host/http.js.map +1 -0
- package/dist/host/socket.d.ts +102 -1
- package/dist/host/socket.d.ts.map +1 -1
- package/dist/host/socket.js +48 -27
- package/dist/host/socket.js.map +1 -1
- package/dist/host-internal.d.ts +5 -0
- package/dist/host-internal.d.ts.map +1 -1
- package/dist/statewire.d.ts +48 -7
- package/dist/statewire.d.ts.map +1 -1
- package/dist/statewire.js +9 -2
- package/dist/statewire.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/src/client/connection.ts +13 -1
- package/src/client/session.ts +352 -70
- package/src/client/sessionStore.ts +8 -15
- package/src/host/http.ts +280 -0
- package/src/host/socket.ts +120 -57
- package/src/host-internal.ts +5 -0
- package/src/statewire.ts +80 -8
- package/src/types.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket.d.ts","names":[],"sources":["../../src/host/socket.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"socket.d.ts","names":[],"sources":["../../src/host/socket.ts"],"mappings":";;;;;;;;;cAgBa,cAAY;KAKpB;EACH;EACA,OAAO;EACP;EACA,OAAO;;KAGJ;EACH;EACA;EACA;EACA;EACA;;KAGU;EACV;EACA;EACA;EACA;EACA;EACA,SAAS;EACT,SAAS;IAAc;IAAY,KAAK;;EACxC,UAAU,YAAY;;KAGZ;EACV;EACA,QAAQ,sBAAsB;EAC9B;EACA;EACA;;KAcG;EAAiB;EAAa,OAAO;;KACrC;EACH;EACA;EACA;EACA;;KAGU;EACV,SAAS;EACT,KAAK;EACL,KAAK;;cAgDM,kBACX,gBACA,mBACC;cAoDU,aAAc,aAAa,mBAAiB;KAmE7C;EACV;EACA;EACA;EACA,WAAW,sBAAsB;EACjC,SAAS,YAAY;EACrB,OAAO,IAAI;EACX,YAAY;EACZ,gBAAgB;EAChB,aAAa,YAAY;EACzB;EACA,UAAU,cAAc;EACxB;EACA;EACA,gBAAgB,kBAAkB;EAClC,WAAW,cAAc;EACzB,cAAc;EACd,SAAS;EACT;EACA,UACE,kBACA,QAAQ,sBAAsB,WAC3B,sBAAsB;EAC3B,SAAS,KAAK,cAAc;EAC5B,SAAS,SAAS,YAAY,QAAQ;EACtC,SAAS,SAAS,YAAY,QAAQ;;cA+C3B,YACX,GAAG,iBACH,MAAM,MACN,eACA;cAgBW,SAAU,GAAG,iBAAiB,MAAM,MAAM;cAsC1C,QAAS,GAAG,iBAAiB,MAAM,cAAc;KAuOlD;EACN;;EACA;EAAiB;IAAO;MAAO;MAAa;;;;cAErC,aACX,GAAG,iBACH,KAAK,WACL,OAAO,gBACN;KAgES;EACV,KAAK;EACL,MAAM;EACN,YAAY,sBAAsB;;cAGvB,WACX,GAAG,iBACH,kBACA,QAAQ,sBAAsB,WAC7B;;;;;;;;cAgLU,sBACX,WAAW,gBAAgB,YAC3B,UAAU,sBAAsB,sBAC/B,sBAAsB"}
|
package/dist/host/socket.js
CHANGED
|
@@ -2,6 +2,7 @@ import { StatewireSendError } from "../types.js";
|
|
|
2
2
|
import { getCommandsHost, getStateHost } from "./host.js";
|
|
3
3
|
import { encodeValue, reviveValue } from "../wire/meta.js";
|
|
4
4
|
import { createMacrotaskPoster } from "../util/macrotask.js";
|
|
5
|
+
import { frames, stream } from "./http.js";
|
|
5
6
|
import { useEffect, useState } from "@assistant-ui/tap/react-shim";
|
|
6
7
|
import { createTapRoot, flushTapSync, useResource } from "@assistant-ui/tap";
|
|
7
8
|
//#region src/host/socket.ts
|
|
@@ -38,13 +39,7 @@ const contextValue = (ops) => {
|
|
|
38
39
|
if (!isPlainObject(value)) throw new Error("context value must be an object");
|
|
39
40
|
return value;
|
|
40
41
|
};
|
|
41
|
-
const
|
|
42
|
-
let frame;
|
|
43
|
-
try {
|
|
44
|
-
frame = JSON.parse(raw);
|
|
45
|
-
} catch {
|
|
46
|
-
return violation("malformed frame");
|
|
47
|
-
}
|
|
42
|
+
const parseFrameValue = (frame, first) => {
|
|
48
43
|
if (!isPlainObject(frame)) return violation("malformed frame");
|
|
49
44
|
if (frame["headers"] === void 0 && frame["ctx"] === void 0 && frame["cmd"] === void 0) return violation("malformed frame");
|
|
50
45
|
let headers = null;
|
|
@@ -80,6 +75,15 @@ const parseFrame = (raw, first) => {
|
|
|
80
75
|
cmd
|
|
81
76
|
};
|
|
82
77
|
};
|
|
78
|
+
const parseFrame = (raw, first) => {
|
|
79
|
+
let frame;
|
|
80
|
+
try {
|
|
81
|
+
frame = JSON.parse(raw);
|
|
82
|
+
} catch {
|
|
83
|
+
return violation("malformed frame");
|
|
84
|
+
}
|
|
85
|
+
return parseFrameValue(frame, first);
|
|
86
|
+
};
|
|
83
87
|
const encodePayload = (payload) => {
|
|
84
88
|
if (payload === void 0) return {};
|
|
85
89
|
const { value, meta } = encodeValue(payload);
|
|
@@ -357,20 +361,19 @@ const admitCmd = (h, rec, statement) => {
|
|
|
357
361
|
});
|
|
358
362
|
});
|
|
359
363
|
};
|
|
360
|
-
const admitFrame = (h,
|
|
361
|
-
const rec = h.clients.get(conn.clientId);
|
|
364
|
+
const admitFrame = (h, rec, frame) => {
|
|
362
365
|
rec.touched = Date.now();
|
|
363
366
|
const through = admittedThrough(rec);
|
|
364
367
|
for (const statement of frame.cmd) if (statement.seq <= through) reemit(h, rec, statement.seq);
|
|
365
368
|
const ctxNew = frame.ctx.filter((s) => s.seq > rec.ctxSeq);
|
|
366
369
|
const cmdNew = frame.cmd.filter((s) => s.seq > through);
|
|
367
|
-
if (ctxNew.length > 0 && ctxNew[0].seq !== rec.ctxSeq + 1 || cmdNew.length > 0 && cmdNew[0].seq !== through + 1) {
|
|
368
|
-
|
|
370
|
+
if (ctxNew.length > 0 && ctxNew[0].seq !== rec.ctxSeq + 1 || cmdNew.length > 0 && cmdNew[0].seq !== through + 1) return {
|
|
371
|
+
type: "refused",
|
|
372
|
+
syn: { syn: {
|
|
369
373
|
seq: through,
|
|
370
374
|
ctxSeq: rec.ctxSeq
|
|
371
|
-
} }
|
|
372
|
-
|
|
373
|
-
}
|
|
375
|
+
} }
|
|
376
|
+
};
|
|
374
377
|
for (const statement of ctxNew) {
|
|
375
378
|
rec.context = statement.value;
|
|
376
379
|
rec.ctxSeq = statement.seq;
|
|
@@ -381,6 +384,7 @@ const admitFrame = (h, conn, frame) => {
|
|
|
381
384
|
dur: true
|
|
382
385
|
});
|
|
383
386
|
scheduleFlush(h);
|
|
387
|
+
return { type: "ok" };
|
|
384
388
|
};
|
|
385
389
|
const message = (h, conn, data) => {
|
|
386
390
|
if (!conn.open || h.disposed || h.finished !== null) return;
|
|
@@ -396,7 +400,9 @@ const message = (h, conn, data) => {
|
|
|
396
400
|
return;
|
|
397
401
|
}
|
|
398
402
|
if (frame.ctx.length === 0 && frame.cmd.length === 0) return;
|
|
399
|
-
|
|
403
|
+
const rec = h.clients.get(conn.clientId);
|
|
404
|
+
const admission = admitFrame(h, rec, frame);
|
|
405
|
+
if (admission.type === "refused") sendTo(h, conn, JSON.stringify(admission.syn));
|
|
400
406
|
};
|
|
401
407
|
const gcClients = (h) => {
|
|
402
408
|
let excess = h.clients.size - h.clientRecordLimit;
|
|
@@ -410,17 +416,12 @@ const gcClients = (h) => {
|
|
|
410
416
|
excess -= 1;
|
|
411
417
|
}
|
|
412
418
|
};
|
|
413
|
-
const
|
|
414
|
-
const dead = {
|
|
415
|
-
clientId,
|
|
416
|
-
message: () => {},
|
|
417
|
-
close: () => {}
|
|
418
|
-
};
|
|
419
|
+
const register = (h, clientId, socket) => {
|
|
419
420
|
const refuse = (code, reason) => {
|
|
420
421
|
try {
|
|
421
422
|
socket.close(code, reason);
|
|
422
423
|
} catch {}
|
|
423
|
-
return
|
|
424
|
+
return null;
|
|
424
425
|
};
|
|
425
426
|
if (h.disposed) return refuse(1001, "disposed");
|
|
426
427
|
if (!CLIENT_ID_RE.test(clientId)) return refuse(1008, "invalid client id");
|
|
@@ -429,7 +430,8 @@ const connect = (h, clientId, socket) => {
|
|
|
429
430
|
try {
|
|
430
431
|
socket.send(JSON.stringify({ fin: h.finished }));
|
|
431
432
|
} catch {}
|
|
432
|
-
|
|
433
|
+
refuse(1e3, "finished");
|
|
434
|
+
return null;
|
|
433
435
|
}
|
|
434
436
|
const known = h.clients.has(clientId);
|
|
435
437
|
const rec = known ? h.clients.get(clientId) : (() => {
|
|
@@ -437,6 +439,7 @@ const connect = (h, clientId, socket) => {
|
|
|
437
439
|
id: clientId,
|
|
438
440
|
lastSeq: 0,
|
|
439
441
|
ctxSeq: 0,
|
|
442
|
+
lease: null,
|
|
440
443
|
touched: Date.now(),
|
|
441
444
|
context: null,
|
|
442
445
|
results: /* @__PURE__ */ new Map(),
|
|
@@ -446,6 +449,7 @@ const connect = (h, clientId, socket) => {
|
|
|
446
449
|
return created;
|
|
447
450
|
})();
|
|
448
451
|
rec.touched = Date.now();
|
|
452
|
+
rec.lease = null;
|
|
449
453
|
for (const other of [...h.conns]) {
|
|
450
454
|
if (other.clientId !== clientId) continue;
|
|
451
455
|
sendTo(h, other, JSON.stringify({ fin: { reason: "superseded" } }));
|
|
@@ -478,9 +482,20 @@ const connect = (h, clientId, socket) => {
|
|
|
478
482
|
};
|
|
479
483
|
sendTo(h, conn, JSON.stringify(hello));
|
|
480
484
|
return {
|
|
485
|
+
rec,
|
|
486
|
+
conn,
|
|
487
|
+
connection: {
|
|
488
|
+
clientId,
|
|
489
|
+
message: (data) => message(h, conn, data),
|
|
490
|
+
close: () => closeConn(h, conn)
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
};
|
|
494
|
+
const connect = (h, clientId, socket) => {
|
|
495
|
+
return register(h, clientId, socket)?.connection ?? {
|
|
481
496
|
clientId,
|
|
482
|
-
message: (
|
|
483
|
-
close: () =>
|
|
497
|
+
message: () => {},
|
|
498
|
+
close: () => {}
|
|
484
499
|
};
|
|
485
500
|
};
|
|
486
501
|
const finish = (h, fin) => {
|
|
@@ -506,6 +521,7 @@ const useSocketHost = (transport, options) => {
|
|
|
506
521
|
heartbeatMs: 15e3,
|
|
507
522
|
clientRecordLimit: CLIENT_RECORD_LIMIT,
|
|
508
523
|
finOnIdle: false,
|
|
524
|
+
authorize: void 0,
|
|
509
525
|
clients: /* @__PURE__ */ new Map(),
|
|
510
526
|
conns: /* @__PURE__ */ new Set(),
|
|
511
527
|
pendingOps: [],
|
|
@@ -524,13 +540,16 @@ const useSocketHost = (transport, options) => {
|
|
|
524
540
|
flush(host);
|
|
525
541
|
}),
|
|
526
542
|
connect: (clientId, socket) => connect(host, clientId, socket),
|
|
527
|
-
finish: (fin) => finish(host, fin)
|
|
543
|
+
finish: (fin) => finish(host, fin),
|
|
544
|
+
stream: (request) => stream(host, request),
|
|
545
|
+
frames: (request_0) => frames(host, request_0)
|
|
528
546
|
};
|
|
529
547
|
return host;
|
|
530
548
|
});
|
|
531
549
|
setHeartbeatMs(h, options?.heartbeatIntervalMs ?? 15e3);
|
|
532
550
|
h.clientRecordLimit = options?.clientRecordLimit ?? CLIENT_RECORD_LIMIT;
|
|
533
551
|
h.finOnIdle = options?.finOnIdle ?? false;
|
|
552
|
+
h.authorize = options?.authorize;
|
|
534
553
|
const value = useResource(transport);
|
|
535
554
|
attach(h, value);
|
|
536
555
|
useEffect(() => {
|
|
@@ -557,6 +576,8 @@ const StatewireSocketHost = (transport, options) => {
|
|
|
557
576
|
let disposed = false;
|
|
558
577
|
return {
|
|
559
578
|
connect: (clientId, socket) => flushTapSync(() => root.getValue().connect(clientId, socket)),
|
|
579
|
+
stream: (request) => flushTapSync(() => root.getValue().stream(request)),
|
|
580
|
+
frames: (request) => flushTapSync(() => root.getValue().frames(request)),
|
|
560
581
|
finish: (fin) => flushTapSync(() => root.getValue().finish(fin)),
|
|
561
582
|
dispose: () => {
|
|
562
583
|
if (disposed) return;
|
|
@@ -567,6 +588,6 @@ const StatewireSocketHost = (transport, options) => {
|
|
|
567
588
|
};
|
|
568
589
|
};
|
|
569
590
|
//#endregion
|
|
570
|
-
export { StatewireSocketHost };
|
|
591
|
+
export { CLIENT_ID_RE, StatewireSocketHost, admitFrame, closeConn, flush, parseFrame, parseFrameValue, register, sendTo };
|
|
571
592
|
|
|
572
593
|
//# sourceMappingURL=socket.js.map
|
package/dist/host/socket.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket.js","names":["useEffect","useState","createTapRoot","flushTapSync","useResource","ResourceElement","getCommandsHost","getStateHost","CommandsHost","CommandsOutcome","StatewireSendError","Statewire","StatewireHost","StatewireHostInternal","encodeValue","reviveValue","createMacrotaskPoster","StatewireMetaEntry","StatewirePath","StatewireCmd","StatewireEnvelope","CLIENT_ID_RE","RESULT_TTL_MS","RESULT_LIMIT","CLIENT_RECORD_LIMIT","WireOp","op","path","value","meta","Admission","seq","method","applied","appliedSent","settled","ClientRec","id","lastSeq","ctxSeq","touched","context","Record","results","Map","ts","cmd","inflight","Conn","clientId","socket","Socket","open","sawFrame","lastSentAt","violation","reason","Object","assign","Error","name","violationReason","error","message","CtxStatement","CmdStatement","params","ParsedFrame","headers","ctx","isPlainObject","Array","isArray","parseLane","length","statements","prev","statement","Number","isInteger","push","contextValue","ops","undefined","parseFrame","raw","first","frame","JSON","parse","values","some","encodePayload","payload","cmdOf","adm","outcome","type","failure","fate","code","String","Host","heartbeatMs","clientRecordLimit","finOnIdle","clients","conns","Set","pendingOps","pendingApplied","pendingCmds","idleWire","finished","Fin","disposed","flushScheduled","heartbeatTimer","ReturnType","setInterval","stateHost","StateHost","commandsHost","current","post","connect","SocketConnection","finish","fin","scheduleFlush","h","recordOp","attach","next","nextState","state","nextCommands","commands","submit","setOnOp","closeConn","conn","delete","close","size","clearInterval","sendTo","data","Date","now","send","ensureHeartbeat","unref","setHeartbeatMs","ms","idleLevel","rec","flush","takeChanged","queueApplied","level","edge","cmds","appliedEntries","get","map","packet","idle","stringify","verdicts","list","set","queueCmd","retain","newest","Math","max","keys","entry","min","admittedThrough","reemit","cached","settleCmd","settleWire","callerOf","Caller","isConnected","rejectStatement","admitCmd","names","methods","sort","caller","onApplied","onSettle","catch","admitFrame","through","ctxNew","filter","s","cmdNew","syn","at","dur","gcClients","excess","live","oldest","a","b","has","dead","refuse","test","known","created","other","add","snapshot","hello","record","resume","suspend","useSocketHost","transport","options","SocketHostOptions","Pick","SocketHost","host","heartbeatIntervalMs","StatewireSocketHost","SocketHostRoot","root","getValue","dispose","unmount"],"sources":["../../src/host/socket.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { createTapRoot, flushTapSync, useResource } from \"@assistant-ui/tap\";\nimport type { ResourceElement } from \"@assistant-ui/tap\";\nimport { getCommandsHost, getStateHost } from \"./host\";\nimport type { CommandsHost, CommandsOutcome } from \"./host\";\nimport { StatewireSendError } from \"../types\";\nimport type { Statewire } from \"../types\";\nimport type { StatewireHost } from \"../host\";\nimport type { StatewireHostInternal } from \"../host-internal\";\nimport { encodeValue, reviveValue } from \"../wire/meta\";\nimport { createMacrotaskPoster } from \"../util/macrotask\";\nimport type { StatewireMetaEntry } from \"../wire/meta\";\nimport type { StatewirePath } from \"../wire/ops\";\nimport type { StatewireCmd, StatewireEnvelope } from \"../client/connection\";\n\nconst CLIENT_ID_RE = /^[A-Za-z0-9._~-]{1,256}$/;\nconst RESULT_TTL_MS = 30_000;\nconst RESULT_LIMIT = 256;\nconst CLIENT_RECORD_LIMIT = 1024;\n\ntype WireOp = {\n op: string;\n path?: StatewirePath;\n value?: unknown;\n meta?: StatewireMetaEntry[];\n};\n\ntype Admission = {\n seq: number;\n method: string;\n applied: boolean;\n appliedSent: boolean;\n settled: boolean;\n};\n\ntype ClientRec = {\n id: string;\n lastSeq: number;\n ctxSeq: number;\n touched: number;\n context: Record<string, unknown> | null;\n results: Map<number, { ts: number; cmd: StatewireCmd }>;\n inflight: Map<number, Admission>;\n};\n\ntype Conn = {\n clientId: string;\n socket: StatewireHostInternal.Socket;\n open: boolean;\n sawFrame: boolean;\n lastSentAt: number;\n};\n\n// ─── frame parsing ──────────────────────────────────────\n\nconst violation = (reason: string): never => {\n throw Object.assign(new Error(reason), { name: \"StatewireViolation\" });\n};\n\nconst violationReason = (error: unknown): string | null =>\n error instanceof Error && error.name === \"StatewireViolation\"\n ? error.message\n : null;\n\ntype CtxStatement = { seq: number; value: Record<string, unknown> };\ntype CmdStatement = {\n seq: number;\n method: string;\n params: unknown;\n meta?: unknown;\n};\n\ntype ParsedFrame = {\n headers: Record<string, string> | null;\n ctx: CtxStatement[];\n cmd: CmdStatement[];\n};\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst parseLane = (value: unknown): Record<string, unknown>[] => {\n if (!Array.isArray(value)) return violation(\"malformed frame\");\n if (value.length === 0) return violation(\"empty lane\");\n const statements: Record<string, unknown>[] = [];\n let prev: number | null = null;\n for (const statement of value) {\n if (!isPlainObject(statement)) return violation(\"malformed statement\");\n const seq = statement[\"seq\"];\n if (typeof seq !== \"number\" || !Number.isInteger(seq) || seq < 1) {\n return violation(\"malformed statement\");\n }\n if (prev !== null && seq !== prev + 1) return violation(\"malformed frame\");\n prev = seq;\n statements.push(statement);\n }\n return statements;\n};\n\n/** v1 ctx statements state the context whole: one root replace with an object value. */\nconst contextValue = (ops: unknown): Record<string, unknown> => {\n if (!Array.isArray(ops) || ops.length !== 1) {\n throw new Error(\"context must be a single-op array\");\n }\n const op = ops[0] as Record<string, unknown> | null;\n if (\n !isPlainObject(op) ||\n op[\"op\"] !== \"replace\" ||\n !Array.isArray(op[\"path\"]) ||\n op[\"path\"].length !== 0\n ) {\n throw new Error(\"context op must be a root replace\");\n }\n const value =\n op[\"meta\"] !== undefined\n ? reviveValue(op[\"value\"], op[\"meta\"])\n : op[\"value\"];\n if (!isPlainObject(value)) {\n throw new Error(\"context value must be an object\");\n }\n return value;\n};\n\nconst parseFrame = (raw: string, first: boolean): ParsedFrame => {\n let frame: unknown;\n try {\n frame = JSON.parse(raw);\n } catch {\n return violation(\"malformed frame\");\n }\n if (!isPlainObject(frame)) return violation(\"malformed frame\");\n if (\n frame[\"headers\"] === undefined &&\n frame[\"ctx\"] === undefined &&\n frame[\"cmd\"] === undefined\n ) {\n return violation(\"malformed frame\");\n }\n let headers: Record<string, string> | null = null;\n if (frame[\"headers\"] !== undefined) {\n if (!first) return violation(\"mid-session headers\");\n const raw = frame[\"headers\"];\n if (\n !isPlainObject(raw) ||\n Object.values(raw).some((value) => typeof value !== \"string\")\n ) {\n return violation(\"malformed headers\");\n }\n headers = raw as Record<string, string>;\n }\n const ctx: CtxStatement[] = [];\n if (frame[\"ctx\"] !== undefined) {\n for (const statement of parseLane(frame[\"ctx\"])) {\n try {\n ctx.push({\n seq: statement[\"seq\"] as number,\n value: contextValue(statement[\"ops\"]),\n });\n } catch {\n return violation(\"invalid-context\");\n }\n }\n }\n const cmd: CmdStatement[] = [];\n if (frame[\"cmd\"] !== undefined) {\n for (const statement of parseLane(frame[\"cmd\"])) {\n const method = statement[\"method\"];\n if (typeof method !== \"string\" || !(\"params\" in statement)) {\n return violation(\"malformed statement\");\n }\n cmd.push({\n seq: statement[\"seq\"] as number,\n method,\n params: statement[\"params\"],\n ...(statement[\"meta\"] !== undefined && { meta: statement[\"meta\"] }),\n });\n }\n }\n return { headers, ctx, cmd };\n};\n\n// ─── verdict encoding ───────────────────────────────────\n\nconst encodePayload = (\n payload: unknown,\n): { payload?: unknown; meta?: StatewireMetaEntry[] } => {\n if (payload === undefined) return {};\n const { value, meta } = encodeValue(payload);\n return { payload: value, ...(meta !== undefined && { meta }) };\n};\n\nconst cmdOf = (adm: Admission, outcome: CommandsOutcome): StatewireCmd => {\n if (outcome.type === \"accepted\") {\n return { seq: adm.seq, type: \"result\", ...encodePayload(outcome.payload) };\n }\n const failure =\n outcome.error instanceof StatewireSendError ? outcome.error.failure : null;\n switch (failure?.fate) {\n case \"failed\":\n return {\n seq: adm.seq,\n type: \"failed\",\n ...(failure.message !== undefined && { message: failure.message }),\n };\n case \"rejected\": {\n if (failure.code !== undefined) {\n const message =\n failure.code === \"unknown-command\"\n ? `unknown method: '${adm.method}'`\n : failure.message;\n return {\n seq: adm.seq,\n type: \"rejected\",\n code: failure.code,\n ...(message !== undefined && { message }),\n };\n }\n return {\n seq: adm.seq,\n type: \"rejected\",\n ...(failure.message !== undefined && { message: failure.message }),\n ...encodePayload(failure.payload),\n };\n }\n default:\n return {\n seq: adm.seq,\n type: \"failed\",\n message:\n outcome.error instanceof Error\n ? outcome.error.message\n : String(outcome.error),\n };\n }\n};\n\n// ─── host ───────────────────────────────────────────────\n\ntype Host = {\n heartbeatMs: number;\n clientRecordLimit: number;\n finOnIdle: boolean;\n clients: Map<string, ClientRec>;\n conns: Set<Conn>;\n pendingOps: WireOp[];\n pendingApplied: Map<string, number[]>;\n pendingCmds: Map<string, StatewireCmd[]>;\n idleWire: boolean;\n finished: StatewireHost.Fin | null;\n disposed: boolean;\n flushScheduled: boolean;\n heartbeatTimer: ReturnType<typeof setInterval> | null;\n stateHost: StatewireHost.StateHost | null;\n commandsHost: CommandsHost | null;\n current: Statewire<unknown> | null;\n post: () => void;\n connect: (\n clientId: string,\n socket: StatewireHostInternal.Socket,\n ) => StatewireHostInternal.SocketConnection;\n finish: (fin: StatewireHost.Fin) => void;\n};\n\nconst scheduleFlush = (h: Host) => {\n if (h.flushScheduled || h.disposed) return;\n h.flushScheduled = true;\n h.post();\n};\n\nconst recordOp = (\n h: Host,\n op: {\n op: string;\n path?: StatewirePath;\n value?: unknown;\n },\n) => {\n if (op.op === \"replace\" || op.op === \"add\") {\n const { value, meta } = encodeValue(op.value);\n h.pendingOps.push({ ...op, value, ...(meta !== undefined && { meta }) });\n } else {\n h.pendingOps.push(op);\n }\n scheduleFlush(h);\n};\n\nconst attach = (h: Host, next: Statewire<unknown>) => {\n if (next === h.current) return;\n h.current = next;\n const nextState =\n typeof next.state === \"object\" && next.state !== null\n ? getStateHost(next.state)\n : undefined;\n const nextCommands = getCommandsHost(next.commands);\n if (!nextState || !nextCommands?.submit) {\n throw new Error(\n \"StatewireSocketHost requires a statewire/host transport (useStatewireState with object state, useStatewireCommands)\",\n );\n }\n if (nextState !== h.stateHost) {\n h.stateHost?.setOnOp(null);\n h.stateHost = nextState;\n h.stateHost.setOnOp((op) => recordOp(h, op));\n }\n h.commandsHost = nextCommands;\n};\n\nconst closeConn = (h: Host, conn: Conn, code?: number, reason?: string) => {\n if (!conn.open) return;\n conn.open = false;\n h.conns.delete(conn);\n try {\n conn.socket.close(code, reason);\n } catch {\n /* peer already gone */\n }\n if (h.conns.size === 0 && h.heartbeatTimer !== null) {\n clearInterval(h.heartbeatTimer);\n h.heartbeatTimer = null;\n }\n};\n\nconst sendTo = (h: Host, conn: Conn, data: string) => {\n if (!conn.open) return;\n conn.lastSentAt = Date.now();\n try {\n conn.socket.send(data);\n } catch {\n closeConn(h, conn);\n }\n};\n\nconst ensureHeartbeat = (h: Host) => {\n if (h.heartbeatTimer !== null || h.heartbeatMs <= 0) return;\n h.heartbeatTimer = setInterval(() => {\n const now = Date.now();\n for (const conn of [...h.conns]) {\n if (now - conn.lastSentAt >= h.heartbeatMs) sendTo(h, conn, \"{}\");\n }\n }, h.heartbeatMs);\n (h.heartbeatTimer as unknown as { unref?: () => void }).unref?.();\n};\n\nconst setHeartbeatMs = (h: Host, ms: number) => {\n if (ms === h.heartbeatMs) return;\n h.heartbeatMs = ms;\n if (h.heartbeatTimer !== null) {\n clearInterval(h.heartbeatTimer);\n h.heartbeatTimer = null;\n ensureHeartbeat(h);\n }\n};\n\nconst idleLevel = (h: Host): boolean => {\n for (const rec of h.clients.values()) {\n if (rec.inflight.size > 0) return false;\n }\n return true;\n};\n\nconst flush = (h: Host, fin?: StatewireHost.Fin) => {\n flushTapSync(() => {});\n h.stateHost?.takeChanged();\n for (const rec of h.clients.values()) {\n for (const adm of rec.inflight.values()) {\n if (adm.applied && !adm.settled && !adm.appliedSent) {\n adm.appliedSent = true;\n queueApplied(h, rec.id, adm.seq);\n }\n }\n }\n const level = idleLevel(h);\n const edge = level !== h.idleWire;\n if (\n h.pendingOps.length === 0 &&\n h.pendingApplied.size === 0 &&\n h.pendingCmds.size === 0 &&\n fin === undefined &&\n !edge\n ) {\n return;\n }\n h.idleWire = level;\n if (fin === undefined && edge && level && h.finOnIdle) {\n fin = { reason: \"idle\" };\n }\n const ops = h.pendingOps;\n const applied = h.pendingApplied;\n const cmds = h.pendingCmds;\n h.pendingOps = [];\n h.pendingApplied = new Map();\n h.pendingCmds = new Map();\n // Applied entries anchor to their effects: piggybacked on the ops packet\n // when one is flushing, standalone otherwise. Terminal verdicts ride their\n // own packet.\n for (const conn of [...h.conns]) {\n const appliedEntries: StatewireCmd[] = (\n applied.get(conn.clientId) ?? []\n ).map((seq) => ({ seq, type: \"applied\" }));\n if (ops.length > 0) {\n const packet: StatewireEnvelope = {\n ops,\n ...(appliedEntries.length > 0 && { cmd: appliedEntries }),\n ...(edge && { idle: level }),\n };\n sendTo(h, conn, JSON.stringify(packet));\n } else if (appliedEntries.length > 0) {\n sendTo(h, conn, JSON.stringify({ cmd: appliedEntries }));\n }\n const verdicts = cmds.get(conn.clientId) ?? [];\n if (verdicts.length > 0) {\n sendTo(h, conn, JSON.stringify({ cmd: verdicts }));\n }\n if (edge && ops.length === 0) {\n sendTo(h, conn, JSON.stringify({ idle: level }));\n }\n if (fin !== undefined) {\n sendTo(h, conn, JSON.stringify({ fin }));\n closeConn(h, conn);\n }\n }\n};\n\nconst queueApplied = (h: Host, clientId: string, seq: number) => {\n const list = h.pendingApplied.get(clientId);\n if (list) list.push(seq);\n else h.pendingApplied.set(clientId, [seq]);\n scheduleFlush(h);\n};\n\nconst queueCmd = (h: Host, clientId: string, cmd: StatewireCmd) => {\n const list = h.pendingCmds.get(clientId);\n if (list) list.push(cmd);\n else h.pendingCmds.set(clientId, [cmd]);\n scheduleFlush(h);\n};\n\nconst retain = (rec: ClientRec, cmd: StatewireCmd) => {\n const now = Date.now();\n rec.results.set(cmd.seq, { ts: now, cmd });\n const newest = Math.max(...rec.results.keys());\n for (const [seq, entry] of rec.results) {\n if (seq !== newest && now - entry.ts > RESULT_TTL_MS) {\n rec.results.delete(seq);\n }\n }\n while (rec.results.size > RESULT_LIMIT) {\n rec.results.delete(Math.min(...rec.results.keys()));\n }\n};\n\n/** Cmd-lane watermark: the last statement admitted, settled or not. */\nconst admittedThrough = (rec: ClientRec): number =>\n Math.max(rec.lastSeq, ...rec.inflight.keys());\n\n// A re-asked seq never re-runs: the stored verdict re-emits, an applied\n// inflight run re-answers applied; one not yet applied answers nothing (its\n// answers ride the stream when ready), an evicted verdict answers expired.\nconst reemit = (h: Host, rec: ClientRec, seq: number) => {\n const cached = rec.results.get(seq);\n if (cached) {\n const newest = Math.max(...rec.results.keys());\n if (seq === newest || Date.now() - cached.ts <= RESULT_TTL_MS) {\n queueCmd(h, rec.id, cached.cmd);\n return;\n }\n rec.results.delete(seq);\n }\n const adm = rec.inflight.get(seq);\n if (adm) {\n if (adm.applied) queueApplied(h, rec.id, seq);\n return;\n }\n queueCmd(h, rec.id, { seq, type: \"expired\" });\n};\n\nconst settleCmd = (\n h: Host,\n rec: ClientRec,\n adm: Admission,\n cmd: StatewireCmd,\n) => {\n if (adm.settled) return;\n adm.settled = true;\n rec.inflight.delete(adm.seq);\n retain(rec, cmd);\n rec.lastSeq = Math.max(rec.lastSeq, adm.seq);\n adm.applied = true;\n queueCmd(h, rec.id, cmd);\n};\n\nconst settleWire = (\n h: Host,\n rec: ClientRec,\n adm: Admission,\n outcome: CommandsOutcome,\n) => settleCmd(h, rec, adm, cmdOf(adm, outcome));\n\nconst callerOf = (h: Host, rec: ClientRec): StatewireHost.Caller => ({\n clientId: rec.id,\n get context() {\n return rec.context;\n },\n get isConnected() {\n if (h.clients.get(rec.id) !== rec) return false;\n for (const conn of h.conns) {\n if (conn.clientId === rec.id && conn.open) return true;\n }\n return false;\n },\n});\n\nconst rejectStatement = (\n h: Host,\n rec: ClientRec,\n adm: Admission,\n message: string,\n) =>\n settleCmd(h, rec, adm, {\n seq: adm.seq,\n type: \"rejected\",\n code: \"invalid-params\",\n message,\n });\n\nconst admitCmd = (h: Host, rec: ClientRec, statement: CmdStatement) => {\n const adm: Admission = {\n seq: statement.seq,\n method: statement.method,\n applied: false,\n appliedSent: false,\n settled: false,\n };\n rec.inflight.set(statement.seq, adm);\n if (!Array.isArray(statement.params)) {\n rejectStatement(h, rec, adm, \"params must be an array\");\n return;\n }\n let params = statement.params;\n if (statement.meta !== undefined) {\n try {\n params = reviveValue(params, statement.meta) as unknown[];\n } catch (error) {\n rejectStatement(\n h,\n rec,\n adm,\n `invalid params: ${(error as Error).message}`,\n );\n return;\n }\n }\n if (statement.method === \"statewire/commands\") {\n if (params.length > 0) {\n rejectStatement(h, rec, adm, \"statewire/commands takes no params\");\n return;\n }\n const names = [\n ...(h.commandsHost?.methods?.() ?? []),\n \"statewire/commands\",\n ].sort();\n settleCmd(h, rec, adm, { seq: adm.seq, type: \"result\", payload: names });\n return;\n }\n h.commandsHost!.submit!({\n method: statement.method,\n params,\n clientId: rec.id,\n caller: callerOf(h, rec),\n onApplied: () => {\n if (adm.settled || adm.applied) return;\n adm.applied = true;\n rec.lastSeq = Math.max(rec.lastSeq, adm.seq);\n scheduleFlush(h);\n },\n onSettle: (outcome) => settleWire(h, rec, adm, outcome),\n }).catch((error: unknown) => {\n if (!adm.settled) settleWire(h, rec, adm, { type: \"rejected\", error });\n });\n};\n\n// One admission law, both lanes: absorbed statements answer from the record,\n// then the new suffix is admitted whole or the frame is refused whole via a\n// {syn} stating both watermarks, nothing consumed. A gap is never a close.\nconst admitFrame = (h: Host, conn: Conn, frame: ParsedFrame) => {\n const rec = h.clients.get(conn.clientId)!;\n rec.touched = Date.now();\n const through = admittedThrough(rec);\n for (const statement of frame.cmd) {\n if (statement.seq <= through) reemit(h, rec, statement.seq);\n }\n const ctxNew = frame.ctx.filter((s) => s.seq > rec.ctxSeq);\n const cmdNew = frame.cmd.filter((s) => s.seq > through);\n if (\n (ctxNew.length > 0 && ctxNew[0]!.seq !== rec.ctxSeq + 1) ||\n (cmdNew.length > 0 && cmdNew[0]!.seq !== through + 1)\n ) {\n sendTo(\n h,\n conn,\n JSON.stringify({ syn: { seq: through, ctxSeq: rec.ctxSeq } }),\n );\n return;\n }\n for (const statement of ctxNew) {\n rec.context = statement.value;\n rec.ctxSeq = statement.seq;\n }\n for (const statement of cmdNew) admitCmd(h, rec, statement);\n if (cmdNew.length > 0) {\n // Memory host: admitted means retained — dur rides the admitting flush.\n queueCmd(h, rec.id, { seq: cmdNew.at(-1)!.seq, dur: true });\n }\n scheduleFlush(h);\n};\n\nconst message = (h: Host, conn: Conn, data: string) => {\n if (!conn.open || h.disposed || h.finished !== null) return;\n const first = !conn.sawFrame;\n conn.sawFrame = true;\n let frame: ParsedFrame;\n try {\n frame = parseFrame(data, first);\n } catch (error) {\n const reason = violationReason(error);\n if (reason === null) throw error;\n closeConn(h, conn, 1008, reason);\n return;\n }\n if (frame.ctx.length === 0 && frame.cmd.length === 0) return;\n admitFrame(h, conn, frame);\n};\n\nconst gcClients = (h: Host) => {\n let excess = h.clients.size - h.clientRecordLimit;\n if (excess <= 0) return;\n const live = new Set([...h.conns].map((conn) => conn.clientId));\n const oldest = [...h.clients.values()].sort((a, b) => a.touched - b.touched);\n for (const rec of oldest) {\n if (excess <= 0) return;\n if (live.has(rec.id) || rec.inflight.size > 0) continue;\n h.clients.delete(rec.id);\n excess -= 1;\n }\n};\n\nconst connect = (\n h: Host,\n clientId: string,\n socket: StatewireHostInternal.Socket,\n): StatewireHostInternal.SocketConnection => {\n const dead: StatewireHostInternal.SocketConnection = {\n clientId,\n message: () => {},\n close: () => {},\n };\n const refuse = (code: number, reason: string) => {\n try {\n socket.close(code, reason);\n } catch {\n /* peer already gone */\n }\n return dead;\n };\n if (h.disposed) return refuse(1001, \"disposed\");\n if (!CLIENT_ID_RE.test(clientId)) return refuse(1008, \"invalid client id\");\n flush(h);\n if (h.finished !== null) {\n try {\n socket.send(JSON.stringify({ fin: h.finished }));\n } catch {\n /* peer already gone */\n }\n return refuse(1000, \"finished\");\n }\n const known = h.clients.has(clientId);\n const rec = known\n ? h.clients.get(clientId)!\n : (() => {\n const created: ClientRec = {\n id: clientId,\n lastSeq: 0,\n ctxSeq: 0,\n touched: Date.now(),\n context: null,\n results: new Map(),\n inflight: new Map(),\n };\n h.clients.set(clientId, created);\n return created;\n })();\n rec.touched = Date.now();\n for (const other of [...h.conns]) {\n if (other.clientId !== clientId) continue;\n sendTo(h, other, JSON.stringify({ fin: { reason: \"superseded\" } }));\n closeConn(h, other);\n }\n const conn: Conn = {\n clientId,\n socket,\n open: true,\n sawFrame: false,\n lastSentAt: 0,\n };\n h.conns.add(conn);\n gcClients(h);\n ensureHeartbeat(h);\n const { value, meta } = encodeValue(h.stateHost!.snapshot());\n const hello: StatewireEnvelope = {\n ops: [\n {\n op: \"replace\",\n path: [],\n value,\n ...(meta !== undefined && { meta }),\n },\n ],\n syn: {\n seq: admittedThrough(rec),\n ctxSeq: rec.ctxSeq,\n record: known ? \"resumed\" : \"fresh\",\n ...(h.idleWire && { idle: true }),\n },\n };\n sendTo(h, conn, JSON.stringify(hello));\n return {\n clientId,\n message: (data) => message(h, conn, data),\n close: () => closeConn(h, conn),\n };\n};\n\nconst finish = (h: Host, fin: StatewireHost.Fin) => {\n if (h.finished !== null || h.disposed) return;\n h.finished = fin;\n flush(h, fin);\n};\n\nconst resume = (h: Host) => {\n h.disposed = false;\n h.stateHost?.setOnOp((op) => recordOp(h, op));\n};\n\nconst suspend = (h: Host) => {\n h.disposed = true;\n if (h.heartbeatTimer !== null) {\n clearInterval(h.heartbeatTimer);\n h.heartbeatTimer = null;\n }\n h.stateHost?.setOnOp(null);\n};\n\nconst useSocketHost = (\n transport: ResourceElement<Statewire>,\n options?: StatewireHostInternal.SocketHostOptions,\n): Pick<StatewireHostInternal.SocketHost, \"connect\" | \"finish\"> => {\n const [h] = useState((): Host => {\n const host: Host = {\n heartbeatMs: 15_000,\n clientRecordLimit: CLIENT_RECORD_LIMIT,\n finOnIdle: false,\n clients: new Map(),\n conns: new Set(),\n pendingOps: [],\n pendingApplied: new Map(),\n pendingCmds: new Map(),\n idleWire: true,\n finished: null,\n disposed: false,\n flushScheduled: false,\n heartbeatTimer: null,\n stateHost: null,\n commandsHost: null,\n current: null,\n post: createMacrotaskPoster(() => {\n host.flushScheduled = false;\n flush(host);\n }),\n connect: (clientId, socket) => connect(host, clientId, socket),\n finish: (fin) => finish(host, fin),\n };\n return host;\n });\n setHeartbeatMs(h, options?.heartbeatIntervalMs ?? 15_000);\n h.clientRecordLimit = options?.clientRecordLimit ?? CLIENT_RECORD_LIMIT;\n h.finOnIdle = options?.finOnIdle ?? false;\n const value = useResource(transport) as Statewire<unknown>;\n attach(h, value);\n useEffect(() => {\n attach(h, value);\n });\n useEffect(() => {\n resume(h);\n return () => suspend(h);\n }, [h]);\n return h;\n};\n\n/**\n * Transport-agnostic WS binding over a statewire/host transport:\n * feed it sockets (anything with `send`/`close`) and raw text frames, and it\n * speaks the Statewire packet protocol — snapshot-first hello with `syn`,\n * statement-lane client frames under one admission law, `{cmd}` answer\n * arrays, per-client-id supersede, idle edges, and `fin` on finish.\n */\nexport const StatewireSocketHost = (\n transport: ResourceElement<Statewire>,\n options?: StatewireHostInternal.SocketHostOptions,\n): StatewireHostInternal.SocketHost => {\n const SocketHostRoot = () => useSocketHost(transport, options);\n const root = createTapRoot(SocketHostRoot);\n let disposed = false;\n return {\n connect: (clientId, socket) =>\n flushTapSync(() => root.getValue().connect(clientId, socket)),\n finish: (fin) => flushTapSync(() => root.getValue().finish(fin)),\n dispose: () => {\n if (disposed) return;\n disposed = true;\n flushTapSync(() => root.getValue().finish({ reason: \"evicted\" }));\n root.unmount();\n },\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"socket.js","names":["useEffect","useState","createTapRoot","flushTapSync","useResource","ResourceElement","getCommandsHost","getStateHost","CommandsHost","CommandsOutcome","StatewireSendError","Statewire","StatewireHost","StatewireHostInternal","encodeValue","reviveValue","createMacrotaskPoster","StatewireMetaEntry","StatewirePath","StatewireCmd","StatewireEnvelope","frames","serveFrames","stream","serveStream","CLIENT_ID_RE","RESULT_TTL_MS","RESULT_LIMIT","CLIENT_RECORD_LIMIT","WireOp","op","path","value","meta","Admission","seq","method","applied","appliedSent","settled","ClientRec","id","lastSeq","ctxSeq","lease","touched","context","Record","results","Map","ts","cmd","inflight","Conn","clientId","socket","Socket","open","sawFrame","lastSentAt","violation","reason","Object","assign","Error","name","violationReason","error","message","CtxStatement","CmdStatement","params","ParsedFrame","headers","ctx","isPlainObject","Array","isArray","parseLane","length","statements","prev","statement","Number","isInteger","push","contextValue","ops","undefined","parseFrameValue","frame","first","raw","values","some","parseFrame","JSON","parse","encodePayload","payload","cmdOf","adm","outcome","type","failure","fate","code","String","SocketHostState","heartbeatMs","clientRecordLimit","finOnIdle","authorize","SocketHostOptions","clients","conns","Set","pendingOps","pendingApplied","pendingCmds","idleWire","finished","Fin","disposed","flushScheduled","heartbeatTimer","ReturnType","setInterval","stateHost","StateHost","commandsHost","current","post","connect","SocketConnection","finish","fin","request","Request","Promise","Response","scheduleFlush","h","recordOp","attach","next","nextState","state","nextCommands","commands","submit","setOnOp","closeConn","conn","delete","close","size","clearInterval","sendTo","data","Date","now","send","ensureHeartbeat","unref","setHeartbeatMs","ms","idleLevel","rec","flush","takeChanged","queueApplied","level","edge","cmds","appliedEntries","get","map","packet","idle","stringify","verdicts","list","set","queueCmd","retain","newest","Math","max","keys","entry","min","admittedThrough","reemit","cached","settleCmd","settleWire","callerOf","Caller","isConnected","rejectStatement","admitCmd","names","methods","sort","caller","onApplied","onSettle","catch","FrameAdmission","syn","admitFrame","through","ctxNew","filter","s","cmdNew","at","dur","admission","gcClients","excess","live","oldest","a","b","has","Registration","connection","register","refuse","test","known","created","other","add","snapshot","hello","record","registration","resume","suspend","useSocketHost","transport","options","Pick","SocketHost","host","heartbeatIntervalMs","StatewireSocketHost","SocketHostRoot","root","getValue","dispose","unmount"],"sources":["../../src/host/socket.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { createTapRoot, flushTapSync, useResource } from \"@assistant-ui/tap\";\nimport type { ResourceElement } from \"@assistant-ui/tap\";\nimport { getCommandsHost, getStateHost } from \"./host\";\nimport type { CommandsHost, CommandsOutcome } from \"./host\";\nimport { StatewireSendError } from \"../types\";\nimport type { Statewire } from \"../types\";\nimport type { StatewireHost } from \"../host\";\nimport type { StatewireHostInternal } from \"../host-internal\";\nimport { encodeValue, reviveValue } from \"../wire/meta\";\nimport { createMacrotaskPoster } from \"../util/macrotask\";\nimport type { StatewireMetaEntry } from \"../wire/meta\";\nimport type { StatewirePath } from \"../wire/ops\";\nimport type { StatewireCmd, StatewireEnvelope } from \"../client/connection\";\nimport { frames as serveFrames, stream as serveStream } from \"./http\";\n\nexport const CLIENT_ID_RE = /^[A-Za-z0-9._~-]{1,256}$/;\nconst RESULT_TTL_MS = 30_000;\nconst RESULT_LIMIT = 256;\nconst CLIENT_RECORD_LIMIT = 1024;\n\ntype WireOp = {\n op: string;\n path?: StatewirePath;\n value?: unknown;\n meta?: StatewireMetaEntry[];\n};\n\ntype Admission = {\n seq: number;\n method: string;\n applied: boolean;\n appliedSent: boolean;\n settled: boolean;\n};\n\nexport type ClientRec = {\n id: string;\n lastSeq: number;\n ctxSeq: number;\n lease: string | null;\n touched: number;\n context: Record<string, unknown> | null;\n results: Map<number, { ts: number; cmd: StatewireCmd }>;\n inflight: Map<number, Admission>;\n};\n\nexport type Conn = {\n clientId: string;\n socket: StatewireHostInternal.Socket;\n open: boolean;\n sawFrame: boolean;\n lastSentAt: number;\n};\n\n// ─── frame parsing ──────────────────────────────────────\n\nconst violation = (reason: string): never => {\n throw Object.assign(new Error(reason), { name: \"StatewireViolation\" });\n};\n\nconst violationReason = (error: unknown): string | null =>\n error instanceof Error && error.name === \"StatewireViolation\"\n ? error.message\n : null;\n\ntype CtxStatement = { seq: number; value: Record<string, unknown> };\ntype CmdStatement = {\n seq: number;\n method: string;\n params: unknown;\n meta?: unknown;\n};\n\nexport type ParsedFrame = {\n headers: Record<string, string> | null;\n ctx: CtxStatement[];\n cmd: CmdStatement[];\n};\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst parseLane = (value: unknown): Record<string, unknown>[] => {\n if (!Array.isArray(value)) return violation(\"malformed frame\");\n if (value.length === 0) return violation(\"empty lane\");\n const statements: Record<string, unknown>[] = [];\n let prev: number | null = null;\n for (const statement of value) {\n if (!isPlainObject(statement)) return violation(\"malformed statement\");\n const seq = statement[\"seq\"];\n if (typeof seq !== \"number\" || !Number.isInteger(seq) || seq < 1) {\n return violation(\"malformed statement\");\n }\n if (prev !== null && seq !== prev + 1) return violation(\"malformed frame\");\n prev = seq;\n statements.push(statement);\n }\n return statements;\n};\n\n/** v1 ctx statements state the context whole: one root replace with an object value. */\nconst contextValue = (ops: unknown): Record<string, unknown> => {\n if (!Array.isArray(ops) || ops.length !== 1) {\n throw new Error(\"context must be a single-op array\");\n }\n const op = ops[0] as Record<string, unknown> | null;\n if (\n !isPlainObject(op) ||\n op[\"op\"] !== \"replace\" ||\n !Array.isArray(op[\"path\"]) ||\n op[\"path\"].length !== 0\n ) {\n throw new Error(\"context op must be a root replace\");\n }\n const value =\n op[\"meta\"] !== undefined\n ? reviveValue(op[\"value\"], op[\"meta\"])\n : op[\"value\"];\n if (!isPlainObject(value)) {\n throw new Error(\"context value must be an object\");\n }\n return value;\n};\n\nexport const parseFrameValue = (\n frame: unknown,\n first: boolean,\n): ParsedFrame => {\n if (!isPlainObject(frame)) return violation(\"malformed frame\");\n if (\n frame[\"headers\"] === undefined &&\n frame[\"ctx\"] === undefined &&\n frame[\"cmd\"] === undefined\n ) {\n return violation(\"malformed frame\");\n }\n let headers: Record<string, string> | null = null;\n if (frame[\"headers\"] !== undefined) {\n if (!first) return violation(\"mid-session headers\");\n const raw = frame[\"headers\"];\n if (\n !isPlainObject(raw) ||\n Object.values(raw).some((value) => typeof value !== \"string\")\n ) {\n return violation(\"malformed headers\");\n }\n headers = raw as Record<string, string>;\n }\n const ctx: CtxStatement[] = [];\n if (frame[\"ctx\"] !== undefined) {\n for (const statement of parseLane(frame[\"ctx\"])) {\n try {\n ctx.push({\n seq: statement[\"seq\"] as number,\n value: contextValue(statement[\"ops\"]),\n });\n } catch {\n return violation(\"invalid-context\");\n }\n }\n }\n const cmd: CmdStatement[] = [];\n if (frame[\"cmd\"] !== undefined) {\n for (const statement of parseLane(frame[\"cmd\"])) {\n const method = statement[\"method\"];\n if (typeof method !== \"string\" || !(\"params\" in statement)) {\n return violation(\"malformed statement\");\n }\n cmd.push({\n seq: statement[\"seq\"] as number,\n method,\n params: statement[\"params\"],\n ...(statement[\"meta\"] !== undefined && { meta: statement[\"meta\"] }),\n });\n }\n }\n return { headers, ctx, cmd };\n};\n\nexport const parseFrame = (raw: string, first: boolean): ParsedFrame => {\n let frame: unknown;\n try {\n frame = JSON.parse(raw);\n } catch {\n return violation(\"malformed frame\");\n }\n return parseFrameValue(frame, first);\n};\n\n// ─── verdict encoding ───────────────────────────────────\n\nconst encodePayload = (\n payload: unknown,\n): { payload?: unknown; meta?: StatewireMetaEntry[] } => {\n if (payload === undefined) return {};\n const { value, meta } = encodeValue(payload);\n return { payload: value, ...(meta !== undefined && { meta }) };\n};\n\nconst cmdOf = (adm: Admission, outcome: CommandsOutcome): StatewireCmd => {\n if (outcome.type === \"accepted\") {\n return { seq: adm.seq, type: \"result\", ...encodePayload(outcome.payload) };\n }\n const failure =\n outcome.error instanceof StatewireSendError ? outcome.error.failure : null;\n switch (failure?.fate) {\n case \"failed\":\n return {\n seq: adm.seq,\n type: \"failed\",\n ...(failure.message !== undefined && { message: failure.message }),\n };\n case \"rejected\": {\n if (failure.code !== undefined) {\n const message =\n failure.code === \"unknown-command\"\n ? `unknown method: '${adm.method}'`\n : failure.message;\n return {\n seq: adm.seq,\n type: \"rejected\",\n code: failure.code,\n ...(message !== undefined && { message }),\n };\n }\n return {\n seq: adm.seq,\n type: \"rejected\",\n ...(failure.message !== undefined && { message: failure.message }),\n ...encodePayload(failure.payload),\n };\n }\n default:\n return {\n seq: adm.seq,\n type: \"failed\",\n message:\n outcome.error instanceof Error\n ? outcome.error.message\n : String(outcome.error),\n };\n }\n};\n\n// ─── host ───────────────────────────────────────────────\n\nexport type SocketHostState = {\n heartbeatMs: number;\n clientRecordLimit: number;\n finOnIdle: boolean;\n authorize: StatewireHostInternal.SocketHostOptions[\"authorize\"];\n clients: Map<string, ClientRec>;\n conns: Set<Conn>;\n pendingOps: WireOp[];\n pendingApplied: Map<string, number[]>;\n pendingCmds: Map<string, StatewireCmd[]>;\n idleWire: boolean;\n finished: StatewireHost.Fin | null;\n disposed: boolean;\n flushScheduled: boolean;\n heartbeatTimer: ReturnType<typeof setInterval> | null;\n stateHost: StatewireHost.StateHost | null;\n commandsHost: CommandsHost | null;\n current: Statewire<unknown> | null;\n post: () => void;\n connect: (\n clientId: string,\n socket: StatewireHostInternal.Socket,\n ) => StatewireHostInternal.SocketConnection;\n finish: (fin: StatewireHost.Fin) => void;\n stream: (request: Request) => Promise<Response>;\n frames: (request: Request) => Promise<Response>;\n};\n\nconst scheduleFlush = (h: SocketHostState) => {\n if (h.flushScheduled || h.disposed) return;\n h.flushScheduled = true;\n h.post();\n};\n\nconst recordOp = (\n h: SocketHostState,\n op: {\n op: string;\n path?: StatewirePath;\n value?: unknown;\n },\n) => {\n if (op.op === \"replace\" || op.op === \"add\") {\n const { value, meta } = encodeValue(op.value);\n h.pendingOps.push({ ...op, value, ...(meta !== undefined && { meta }) });\n } else {\n h.pendingOps.push(op);\n }\n scheduleFlush(h);\n};\n\nconst attach = (h: SocketHostState, next: Statewire<unknown>) => {\n if (next === h.current) return;\n h.current = next;\n const nextState =\n typeof next.state === \"object\" && next.state !== null\n ? getStateHost(next.state)\n : undefined;\n const nextCommands = getCommandsHost(next.commands);\n if (!nextState || !nextCommands?.submit) {\n throw new Error(\n \"StatewireSocketHost requires a statewire/host transport (useStatewireState with object state, useStatewireCommands)\",\n );\n }\n if (nextState !== h.stateHost) {\n h.stateHost?.setOnOp(null);\n h.stateHost = nextState;\n h.stateHost.setOnOp((op) => recordOp(h, op));\n }\n h.commandsHost = nextCommands;\n};\n\nexport const closeConn = (\n h: SocketHostState,\n conn: Conn,\n code?: number,\n reason?: string,\n) => {\n if (!conn.open) return;\n conn.open = false;\n h.conns.delete(conn);\n try {\n conn.socket.close(code, reason);\n } catch {\n /* peer already gone */\n }\n if (h.conns.size === 0 && h.heartbeatTimer !== null) {\n clearInterval(h.heartbeatTimer);\n h.heartbeatTimer = null;\n }\n};\n\nexport const sendTo = (h: SocketHostState, conn: Conn, data: string) => {\n if (!conn.open) return;\n conn.lastSentAt = Date.now();\n try {\n conn.socket.send(data);\n } catch {\n closeConn(h, conn);\n }\n};\n\nconst ensureHeartbeat = (h: SocketHostState) => {\n if (h.heartbeatTimer !== null || h.heartbeatMs <= 0) return;\n h.heartbeatTimer = setInterval(() => {\n const now = Date.now();\n for (const conn of [...h.conns]) {\n if (now - conn.lastSentAt >= h.heartbeatMs) sendTo(h, conn, \"{}\");\n }\n }, h.heartbeatMs);\n (h.heartbeatTimer as unknown as { unref?: () => void }).unref?.();\n};\n\nconst setHeartbeatMs = (h: SocketHostState, ms: number) => {\n if (ms === h.heartbeatMs) return;\n h.heartbeatMs = ms;\n if (h.heartbeatTimer !== null) {\n clearInterval(h.heartbeatTimer);\n h.heartbeatTimer = null;\n ensureHeartbeat(h);\n }\n};\n\nconst idleLevel = (h: SocketHostState): boolean => {\n for (const rec of h.clients.values()) {\n if (rec.inflight.size > 0) return false;\n }\n return true;\n};\n\nexport const flush = (h: SocketHostState, fin?: StatewireHost.Fin) => {\n flushTapSync(() => {});\n h.stateHost?.takeChanged();\n for (const rec of h.clients.values()) {\n for (const adm of rec.inflight.values()) {\n if (adm.applied && !adm.settled && !adm.appliedSent) {\n adm.appliedSent = true;\n queueApplied(h, rec.id, adm.seq);\n }\n }\n }\n const level = idleLevel(h);\n const edge = level !== h.idleWire;\n if (\n h.pendingOps.length === 0 &&\n h.pendingApplied.size === 0 &&\n h.pendingCmds.size === 0 &&\n fin === undefined &&\n !edge\n ) {\n return;\n }\n h.idleWire = level;\n if (fin === undefined && edge && level && h.finOnIdle) {\n fin = { reason: \"idle\" };\n }\n const ops = h.pendingOps;\n const applied = h.pendingApplied;\n const cmds = h.pendingCmds;\n h.pendingOps = [];\n h.pendingApplied = new Map();\n h.pendingCmds = new Map();\n // Applied entries anchor to their effects: piggybacked on the ops packet\n // when one is flushing, standalone otherwise. Terminal verdicts ride their\n // own packet.\n for (const conn of [...h.conns]) {\n const appliedEntries: StatewireCmd[] = (\n applied.get(conn.clientId) ?? []\n ).map((seq) => ({ seq, type: \"applied\" }));\n if (ops.length > 0) {\n const packet: StatewireEnvelope = {\n ops,\n ...(appliedEntries.length > 0 && { cmd: appliedEntries }),\n ...(edge && { idle: level }),\n };\n sendTo(h, conn, JSON.stringify(packet));\n } else if (appliedEntries.length > 0) {\n sendTo(h, conn, JSON.stringify({ cmd: appliedEntries }));\n }\n const verdicts = cmds.get(conn.clientId) ?? [];\n if (verdicts.length > 0) {\n sendTo(h, conn, JSON.stringify({ cmd: verdicts }));\n }\n if (edge && ops.length === 0) {\n sendTo(h, conn, JSON.stringify({ idle: level }));\n }\n if (fin !== undefined) {\n sendTo(h, conn, JSON.stringify({ fin }));\n closeConn(h, conn);\n }\n }\n};\n\nconst queueApplied = (h: SocketHostState, clientId: string, seq: number) => {\n const list = h.pendingApplied.get(clientId);\n if (list) list.push(seq);\n else h.pendingApplied.set(clientId, [seq]);\n scheduleFlush(h);\n};\n\nconst queueCmd = (h: SocketHostState, clientId: string, cmd: StatewireCmd) => {\n const list = h.pendingCmds.get(clientId);\n if (list) list.push(cmd);\n else h.pendingCmds.set(clientId, [cmd]);\n scheduleFlush(h);\n};\n\nconst retain = (rec: ClientRec, cmd: StatewireCmd) => {\n const now = Date.now();\n rec.results.set(cmd.seq, { ts: now, cmd });\n const newest = Math.max(...rec.results.keys());\n for (const [seq, entry] of rec.results) {\n if (seq !== newest && now - entry.ts > RESULT_TTL_MS) {\n rec.results.delete(seq);\n }\n }\n while (rec.results.size > RESULT_LIMIT) {\n rec.results.delete(Math.min(...rec.results.keys()));\n }\n};\n\n/** Cmd-lane watermark: the last statement admitted, settled or not. */\nconst admittedThrough = (rec: ClientRec): number =>\n Math.max(rec.lastSeq, ...rec.inflight.keys());\n\n// A re-asked seq never re-runs: the stored verdict re-emits, an applied\n// inflight run re-answers applied; one not yet applied answers nothing (its\n// answers ride the stream when ready), an evicted verdict answers expired.\nconst reemit = (h: SocketHostState, rec: ClientRec, seq: number) => {\n const cached = rec.results.get(seq);\n if (cached) {\n const newest = Math.max(...rec.results.keys());\n if (seq === newest || Date.now() - cached.ts <= RESULT_TTL_MS) {\n queueCmd(h, rec.id, cached.cmd);\n return;\n }\n rec.results.delete(seq);\n }\n const adm = rec.inflight.get(seq);\n if (adm) {\n if (adm.applied) queueApplied(h, rec.id, seq);\n return;\n }\n queueCmd(h, rec.id, { seq, type: \"expired\" });\n};\n\nconst settleCmd = (\n h: SocketHostState,\n rec: ClientRec,\n adm: Admission,\n cmd: StatewireCmd,\n) => {\n if (adm.settled) return;\n adm.settled = true;\n rec.inflight.delete(adm.seq);\n retain(rec, cmd);\n rec.lastSeq = Math.max(rec.lastSeq, adm.seq);\n adm.applied = true;\n queueCmd(h, rec.id, cmd);\n};\n\nconst settleWire = (\n h: SocketHostState,\n rec: ClientRec,\n adm: Admission,\n outcome: CommandsOutcome,\n) => settleCmd(h, rec, adm, cmdOf(adm, outcome));\n\nconst callerOf = (\n h: SocketHostState,\n rec: ClientRec,\n): StatewireHost.Caller => ({\n clientId: rec.id,\n get context() {\n return rec.context;\n },\n get isConnected() {\n if (h.clients.get(rec.id) !== rec) return false;\n for (const conn of h.conns) {\n if (conn.clientId === rec.id && conn.open) return true;\n }\n return false;\n },\n});\n\nconst rejectStatement = (\n h: SocketHostState,\n rec: ClientRec,\n adm: Admission,\n message: string,\n) =>\n settleCmd(h, rec, adm, {\n seq: adm.seq,\n type: \"rejected\",\n code: \"invalid-params\",\n message,\n });\n\nconst admitCmd = (\n h: SocketHostState,\n rec: ClientRec,\n statement: CmdStatement,\n) => {\n const adm: Admission = {\n seq: statement.seq,\n method: statement.method,\n applied: false,\n appliedSent: false,\n settled: false,\n };\n rec.inflight.set(statement.seq, adm);\n if (!Array.isArray(statement.params)) {\n rejectStatement(h, rec, adm, \"params must be an array\");\n return;\n }\n let params = statement.params;\n if (statement.meta !== undefined) {\n try {\n params = reviveValue(params, statement.meta) as unknown[];\n } catch (error) {\n rejectStatement(\n h,\n rec,\n adm,\n `invalid params: ${(error as Error).message}`,\n );\n return;\n }\n }\n if (statement.method === \"statewire/commands\") {\n if (params.length > 0) {\n rejectStatement(h, rec, adm, \"statewire/commands takes no params\");\n return;\n }\n const names = [\n ...(h.commandsHost?.methods?.() ?? []),\n \"statewire/commands\",\n ].sort();\n settleCmd(h, rec, adm, { seq: adm.seq, type: \"result\", payload: names });\n return;\n }\n h.commandsHost!.submit!({\n method: statement.method,\n params,\n clientId: rec.id,\n caller: callerOf(h, rec),\n onApplied: () => {\n if (adm.settled || adm.applied) return;\n adm.applied = true;\n rec.lastSeq = Math.max(rec.lastSeq, adm.seq);\n scheduleFlush(h);\n },\n onSettle: (outcome) => settleWire(h, rec, adm, outcome),\n }).catch((error: unknown) => {\n if (!adm.settled) settleWire(h, rec, adm, { type: \"rejected\", error });\n });\n};\n\n// One admission law, both lanes: absorbed statements answer from the record,\n// then the new suffix is admitted whole or the frame is refused whole via a\n// {syn} stating both watermarks, nothing consumed. A gap is never a close.\nexport type FrameAdmission =\n | { type: \"ok\" }\n | { type: \"refused\"; syn: { syn: { seq: number; ctxSeq: number } } };\n\nexport const admitFrame = (\n h: SocketHostState,\n rec: ClientRec,\n frame: ParsedFrame,\n): FrameAdmission => {\n rec.touched = Date.now();\n const through = admittedThrough(rec);\n for (const statement of frame.cmd) {\n if (statement.seq <= through) reemit(h, rec, statement.seq);\n }\n const ctxNew = frame.ctx.filter((s) => s.seq > rec.ctxSeq);\n const cmdNew = frame.cmd.filter((s) => s.seq > through);\n if (\n (ctxNew.length > 0 && ctxNew[0]!.seq !== rec.ctxSeq + 1) ||\n (cmdNew.length > 0 && cmdNew[0]!.seq !== through + 1)\n ) {\n return {\n type: \"refused\",\n syn: { syn: { seq: through, ctxSeq: rec.ctxSeq } },\n };\n }\n for (const statement of ctxNew) {\n rec.context = statement.value;\n rec.ctxSeq = statement.seq;\n }\n for (const statement of cmdNew) admitCmd(h, rec, statement);\n if (cmdNew.length > 0) {\n // Memory host: admitted means retained — dur rides the admitting flush.\n queueCmd(h, rec.id, { seq: cmdNew.at(-1)!.seq, dur: true });\n }\n scheduleFlush(h);\n return { type: \"ok\" };\n};\n\nconst message = (h: SocketHostState, conn: Conn, data: string) => {\n if (!conn.open || h.disposed || h.finished !== null) return;\n const first = !conn.sawFrame;\n conn.sawFrame = true;\n let frame: ParsedFrame;\n try {\n frame = parseFrame(data, first);\n } catch (error) {\n const reason = violationReason(error);\n if (reason === null) throw error;\n closeConn(h, conn, 1008, reason);\n return;\n }\n if (frame.ctx.length === 0 && frame.cmd.length === 0) return;\n const rec = h.clients.get(conn.clientId)!;\n const admission = admitFrame(h, rec, frame);\n if (admission.type === \"refused\") {\n sendTo(h, conn, JSON.stringify(admission.syn));\n }\n};\n\nconst gcClients = (h: SocketHostState) => {\n let excess = h.clients.size - h.clientRecordLimit;\n if (excess <= 0) return;\n const live = new Set([...h.conns].map((conn) => conn.clientId));\n const oldest = [...h.clients.values()].sort((a, b) => a.touched - b.touched);\n for (const rec of oldest) {\n if (excess <= 0) return;\n if (live.has(rec.id) || rec.inflight.size > 0) continue;\n h.clients.delete(rec.id);\n excess -= 1;\n }\n};\n\nexport type Registration = {\n rec: ClientRec;\n conn: Conn;\n connection: StatewireHostInternal.SocketConnection;\n};\n\nexport const register = (\n h: SocketHostState,\n clientId: string,\n socket: StatewireHostInternal.Socket,\n): Registration | null => {\n const refuse = (code: number, reason: string) => {\n try {\n socket.close(code, reason);\n } catch {\n /* peer already gone */\n }\n return null;\n };\n if (h.disposed) return refuse(1001, \"disposed\");\n if (!CLIENT_ID_RE.test(clientId)) return refuse(1008, \"invalid client id\");\n flush(h);\n if (h.finished !== null) {\n try {\n socket.send(JSON.stringify({ fin: h.finished }));\n } catch {\n /* peer already gone */\n }\n refuse(1000, \"finished\");\n return null;\n }\n const known = h.clients.has(clientId);\n const rec = known\n ? h.clients.get(clientId)!\n : (() => {\n const created: ClientRec = {\n id: clientId,\n lastSeq: 0,\n ctxSeq: 0,\n lease: null,\n touched: Date.now(),\n context: null,\n results: new Map(),\n inflight: new Map(),\n };\n h.clients.set(clientId, created);\n return created;\n })();\n rec.touched = Date.now();\n rec.lease = null;\n for (const other of [...h.conns]) {\n if (other.clientId !== clientId) continue;\n sendTo(h, other, JSON.stringify({ fin: { reason: \"superseded\" } }));\n closeConn(h, other);\n }\n const conn: Conn = {\n clientId,\n socket,\n open: true,\n sawFrame: false,\n lastSentAt: 0,\n };\n h.conns.add(conn);\n gcClients(h);\n ensureHeartbeat(h);\n const { value, meta } = encodeValue(h.stateHost!.snapshot());\n const hello: StatewireEnvelope = {\n ops: [\n {\n op: \"replace\",\n path: [],\n value,\n ...(meta !== undefined && { meta }),\n },\n ],\n syn: {\n seq: admittedThrough(rec),\n ctxSeq: rec.ctxSeq,\n record: known ? \"resumed\" : \"fresh\",\n ...(h.idleWire && { idle: true }),\n },\n };\n sendTo(h, conn, JSON.stringify(hello));\n const connection: StatewireHostInternal.SocketConnection = {\n clientId,\n message: (data) => message(h, conn, data),\n close: () => closeConn(h, conn),\n };\n return { rec, conn, connection };\n};\n\nconst connect = (\n h: SocketHostState,\n clientId: string,\n socket: StatewireHostInternal.Socket,\n): StatewireHostInternal.SocketConnection => {\n const registration = register(h, clientId, socket);\n return (\n registration?.connection ?? {\n clientId,\n message: () => {},\n close: () => {},\n }\n );\n};\n\nconst finish = (h: SocketHostState, fin: StatewireHost.Fin) => {\n if (h.finished !== null || h.disposed) return;\n h.finished = fin;\n flush(h, fin);\n};\n\nconst resume = (h: SocketHostState) => {\n h.disposed = false;\n h.stateHost?.setOnOp((op) => recordOp(h, op));\n};\n\nconst suspend = (h: SocketHostState) => {\n h.disposed = true;\n if (h.heartbeatTimer !== null) {\n clearInterval(h.heartbeatTimer);\n h.heartbeatTimer = null;\n }\n h.stateHost?.setOnOp(null);\n};\n\nconst useSocketHost = (\n transport: ResourceElement<Statewire>,\n options?: StatewireHostInternal.SocketHostOptions,\n): Pick<\n StatewireHostInternal.SocketHost,\n \"connect\" | \"finish\" | \"stream\" | \"frames\"\n> => {\n const [h] = useState((): SocketHostState => {\n const host: SocketHostState = {\n heartbeatMs: 15_000,\n clientRecordLimit: CLIENT_RECORD_LIMIT,\n finOnIdle: false,\n authorize: undefined,\n clients: new Map(),\n conns: new Set(),\n pendingOps: [],\n pendingApplied: new Map(),\n pendingCmds: new Map(),\n idleWire: true,\n finished: null,\n disposed: false,\n flushScheduled: false,\n heartbeatTimer: null,\n stateHost: null,\n commandsHost: null,\n current: null,\n post: createMacrotaskPoster(() => {\n host.flushScheduled = false;\n flush(host);\n }),\n connect: (clientId, socket) => connect(host, clientId, socket),\n finish: (fin) => finish(host, fin),\n stream: (request) => serveStream(host, request),\n frames: (request) => serveFrames(host, request),\n };\n return host;\n });\n setHeartbeatMs(h, options?.heartbeatIntervalMs ?? 15_000);\n h.clientRecordLimit = options?.clientRecordLimit ?? CLIENT_RECORD_LIMIT;\n h.finOnIdle = options?.finOnIdle ?? false;\n h.authorize = options?.authorize;\n const value = useResource(transport) as Statewire<unknown>;\n attach(h, value);\n useEffect(() => {\n attach(h, value);\n });\n useEffect(() => {\n resume(h);\n return () => suspend(h);\n }, [h]);\n return h;\n};\n\n/**\n * Transport-agnostic WS binding over a statewire/host transport:\n * feed it sockets (anything with `send`/`close`) and raw text frames, and it\n * speaks the Statewire packet protocol — snapshot-first hello with `syn`,\n * statement-lane client frames under one admission law, `{cmd}` answer\n * arrays, per-client-id supersede, idle edges, and `fin` on finish.\n */\nexport const StatewireSocketHost = (\n transport: ResourceElement<Statewire>,\n options?: StatewireHostInternal.SocketHostOptions,\n): StatewireHostInternal.SocketHost => {\n const SocketHostRoot = () => useSocketHost(transport, options);\n const root = createTapRoot(SocketHostRoot);\n let disposed = false;\n return {\n connect: (clientId, socket) =>\n flushTapSync(() => root.getValue().connect(clientId, socket)),\n stream: (request) => flushTapSync(() => root.getValue().stream(request)),\n frames: (request) => flushTapSync(() => root.getValue().frames(request)),\n finish: (fin) => flushTapSync(() => root.getValue().finish(fin)),\n dispose: () => {\n if (disposed) return;\n disposed = true;\n flushTapSync(() => root.getValue().finish({ reason: \"evicted\" }));\n root.unmount();\n },\n };\n};\n"],"mappings":"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"}
|
package/dist/host-internal.d.ts
CHANGED
|
@@ -14,10 +14,15 @@ declare namespace StatewireHostInternal {
|
|
|
14
14
|
};
|
|
15
15
|
type SocketHost = {
|
|
16
16
|
connect: (clientId: string, socket: Socket) => SocketConnection;
|
|
17
|
+
stream: (request: Request) => Promise<Response>;
|
|
18
|
+
frames: (request: Request) => Promise<Response>;
|
|
17
19
|
finish: (fin: StatewireHost.Fin) => void;
|
|
18
20
|
dispose: () => void;
|
|
19
21
|
};
|
|
20
22
|
type SocketHostOptions = {
|
|
23
|
+
authorize?: (ctx: {
|
|
24
|
+
headers: Record<string, string>;
|
|
25
|
+
}) => void | Promise<void>;
|
|
21
26
|
heartbeatIntervalMs?: number;
|
|
22
27
|
/** LRU cap on stored client records; live or in-flight records are exempt. */
|
|
23
28
|
clientRecordLimit?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-internal.d.ts","names":[],"sources":["../src/host-internal.ts"],"mappings":";;;;kBAIiB;OACH;IACV,OAAO;IACP,QAAQ,eAAe;;OAGb;aACD;IACT,UAAU;IACV;;OAGU;IACV,UAAU,kBAAkB,QAAQ,WAAW;IAC/C,SAAS,KAAK,cAAc;IAC5B;;OAGU;IACV;;IAEA;;IAEA;;OAGU,MAAM,cAAc"}
|
|
1
|
+
{"version":3,"file":"host-internal.d.ts","names":[],"sources":["../src/host-internal.ts"],"mappings":";;;;kBAIiB;OACH;IACV,OAAO;IACP,QAAQ,eAAe;;OAGb;aACD;IACT,UAAU;IACV;;OAGU;IACV,UAAU,kBAAkB,QAAQ,WAAW;IAC/C,SAAS,SAAS,YAAY,QAAQ;IACtC,SAAS,SAAS,YAAY,QAAQ;IACtC,SAAS,KAAK,cAAc;IAC5B;;OAGU;IACV,aAAa;MACX,SAAS;iBACE;IACb;;IAEA;;IAEA;;OAGU,MAAM,cAAc"}
|
package/dist/statewire.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { StatewireSessionStoreOptions } from "./client/sessionStore.js";
|
|
2
1
|
import { Statewire } from "./types.js";
|
|
3
2
|
import { ResourceElement } from "@assistant-ui/tap";
|
|
4
3
|
//#region src/statewire.d.ts
|
|
@@ -19,6 +18,10 @@ declare class StatewireClient<TState, TCommands extends Statewire.Commands = Sta
|
|
|
19
18
|
declare namespace StatewireClient {
|
|
20
19
|
type Options<TState, TCommands extends Statewire.Commands = Statewire.Commands> = {
|
|
21
20
|
transport: ResourceElement<Statewire<TState, TCommands>>;
|
|
21
|
+
/** Durable session: the mounted storage holds this client's lane identity and its un-durable commands, which resend on the next attach. */
|
|
22
|
+
storage?: {
|
|
23
|
+
session?: ResourceElement<SessionStorage> | undefined;
|
|
24
|
+
} | undefined;
|
|
22
25
|
/** Served as the canonical state until the attach snapshot; own commands fold over it. */
|
|
23
26
|
initialState?: TState;
|
|
24
27
|
optimistic?: Optimistic<NonNullable<TState>, TCommands>;
|
|
@@ -84,10 +87,12 @@ declare namespace StatewireClient {
|
|
|
84
87
|
/**
|
|
85
88
|
* fate: "aborted" = never sent, "rejected" = the server refused it
|
|
86
89
|
* (`code` "unknown-command" | "invalid-params" for the static rejects,
|
|
87
|
-
* code-less for handler rejects), "lost" = sent but
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
90
|
+
* code-less for handler rejects), "lost" = sent but no verdict will ever
|
|
91
|
+
* arrive — the record is gone (`record: "fresh"`) or the channel died: it
|
|
92
|
+
* may or may not have run, "failed" = the handler ran and threw,
|
|
93
|
+
* "crashed" = the handler died involuntarily, "expired" = settled —
|
|
94
|
+
* result, rejected, failed, or crashed — but the verdict outlived the
|
|
95
|
+
* server's retention.
|
|
91
96
|
* cause is which channel settled it: "server" = the server's answer;
|
|
92
97
|
* the rest are channel deaths the client settled locally.
|
|
93
98
|
*/
|
|
@@ -115,11 +120,46 @@ declare namespace StatewireClient {
|
|
|
115
120
|
fate: "lost";
|
|
116
121
|
cause: "unmount" | "stream-gone" | "session-lost" | "superseded";
|
|
117
122
|
};
|
|
123
|
+
/** A command as persisted while un-durable: its lane seq plus the wire statement. */
|
|
124
|
+
type SessionCommand = {
|
|
125
|
+
seq: number;
|
|
126
|
+
method: string;
|
|
127
|
+
params: unknown[];
|
|
128
|
+
meta?: unknown;
|
|
129
|
+
};
|
|
130
|
+
/** The persisted session: the lane identity, the lane clocks, and the commands the server has not marked durable. */
|
|
131
|
+
type SessionRecord = {
|
|
132
|
+
clientId: string;
|
|
133
|
+
nextSeq: number;
|
|
134
|
+
ctxNextSeq: number;
|
|
135
|
+
commands: readonly SessionCommand[];
|
|
136
|
+
/** Terminal verdicts parked for the sharer that minted the seq (shared storage only). */
|
|
137
|
+
verdicts?: readonly SessionVerdict[];
|
|
138
|
+
};
|
|
139
|
+
/** A terminal verdict one sharer's connection carried, parked for the sharer awaiting the seq. */
|
|
140
|
+
type SessionVerdict = {
|
|
141
|
+
seq: number;
|
|
142
|
+
type: "result" | "rejected" | "failed" | "crashed" | "expired";
|
|
143
|
+
code?: string;
|
|
144
|
+
message?: string;
|
|
145
|
+
payload?: unknown;
|
|
146
|
+
meta?: unknown;
|
|
147
|
+
};
|
|
148
|
+
/** Session storage seam; `save` must persist synchronously in the caller's flow. */
|
|
149
|
+
type SessionStorage = {
|
|
150
|
+
load: () => SessionRecord | null;
|
|
151
|
+
save: (record: SessionRecord) => void;
|
|
152
|
+
/** Serializes record mutations across sharers; presence marks the storage shared. */
|
|
153
|
+
lock?: <T>(fn: () => T | Promise<T>) => Promise<T>;
|
|
154
|
+
/** Notifies of record saves by other sharers; returns unsubscribe. Required alongside `lock`. */
|
|
155
|
+
subscribe?: (listener: () => void) => () => void;
|
|
156
|
+
};
|
|
118
157
|
/** Static headers, or a resolver invoked per request/attach; `staleAuth` is true right after an auth rejection. */
|
|
119
158
|
type HeadersOption = HeadersInit | ((ctx: {
|
|
120
159
|
staleAuth: boolean;
|
|
121
160
|
}) => HeadersInit | Promise<HeadersInit>);
|
|
122
|
-
type SSEOptions =
|
|
161
|
+
type SSEOptions = {
|
|
162
|
+
sessionStore?: SessionStorage | undefined;
|
|
123
163
|
url: string;
|
|
124
164
|
headers?: HeadersOption;
|
|
125
165
|
fetch?: typeof globalThis.fetch;
|
|
@@ -127,7 +167,8 @@ declare namespace StatewireClient {
|
|
|
127
167
|
/** The broadcast idle flag's rising edge: the instance went quiet. */
|
|
128
168
|
onDone?: () => void;
|
|
129
169
|
};
|
|
130
|
-
type WSOptions =
|
|
170
|
+
type WSOptions = {
|
|
171
|
+
sessionStore?: SessionStorage | undefined;
|
|
131
172
|
url: string;
|
|
132
173
|
headers?: HeadersOption;
|
|
133
174
|
protocols?: string | string[];
|
package/dist/statewire.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"statewire.d.ts","names":[],"sources":["../src/statewire.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"statewire.d.ts","names":[],"sources":["../src/statewire.ts"],"mappings":";;;cA+Ba,eACX,QACA,kBAAkB,UAAU,WAAW,UAAU,UAEjD,SAAS,gBAAgB,QAAQ,QAAQ,eACxC,gBAAgB,QAAQ,QAAQ;cA8CtB,oBACX,QACA,kBAAkB,UAAU,WAAW,UAAU,UAEjD,SAAS,gBAAgB,QAAQ,QAAQ,eACxC,gBAAgB,gBAAgB,QAAQ,QAAQ;cAGtC,gBACX,QACA,kBAAkB,UAAU,WAAW,UAAU;mBAEhC;UACT;EAEI,YAAA,SAAS,gBAAgB,QAAQ,QAAQ;MAKjD,SAAS;MAIT,cAAc,gBAAgB;MAI9B,YAAY,UAAU,cAAc;MAIpC;EAIJ,WAAW,OAAO;EAIlB,UAAU;EAIV;;kBAOe;OACH,QACV,QACA,kBAAkB,UAAU,WAAW,UAAU;IAEjD,WAAW,gBAAgB,UAAU,QAAQ;;IAE7C;MACM,UAAU,gBAAgB;;;IAGhC,eAAe;IACf,aAAa,WAAW,YAAY,SAAS;IAC7C,cAAc,YAAY,YAAY;IACtC,iBACE,OAAO,YAAY,SACnB;MAAO,SAAS,UAAU;;IAE5B,mBACE,QAAQ,UAAU,cAAc,YAChC;MAAO,OAAO,YAAY;;IAE5B,sBAAsB,YAAY;;OAGxB,QACV,QACA,kBAAkB,UAAU,WAAW,UAAU;;IAGjD,OAAO;;IAEP,gBAAgB;IAChB,YAAY;IACZ,UAAU,UAAU,cAAc;;IAElC;;IAEA,aAAa,OAAO;;OAGV;IACN;;IACA;;IACA;;IAEA;IACA;IACA;IACA;IACA;IACA;;IAEA;IAAgB;IAAkB;;OAC5B,aAAa;IAAoB;;OACjC,mBAAmB;;;;;OAMnB,WACV,QACA,kBAAkB,UAAU,WAAW,UAAU,aAEjD,OAAO,QACP,SAAS,UAAU,QAAQ,YAC3B,KAAK;OAGK;IACV;IACA;IACA;;;;;;;OAQU,YAAY,WAAW,OAAO;;;;;;;;;;;;;OAc9B;IAEN;IACA;IACA;IACA;IACA;;IAEA;IAAgB;IAAiB;;IACjC;IAAiB;;IACjB;IAAiB;;IAEjB;IACA;IACA;;IAGA;IACA;;;OAIM;IACV;IACA;IACA;IACA;;;OAIU;IACV;IACA;IACA;IACA,mBAAmB;;IAEnB,oBAAoB;;;OAIV;IACV;IACA;IACA;IACA;IACA;IACA;;;OAIU;IACV,YAAY;IACZ,OAAO,QAAQ;;IAEf,QAAQ,GAAG,UAAU,IAAI,QAAQ,OAAO,QAAQ;;IAEhD,aAAa;;;OAIH,gBACR,gBACE;IAAO;QAAyB,cAAc,QAAQ;OAEhD;IACV,eAAe;IACf;IACA,UAAU;IACV,eAAe,WAAW;IAC1B,YAAY,YAAY;;IAExB;;OAGU;IACV,eAAe;IACf;IACA,UAAU;IACV;IACA,QACI,gCACO,yBAAyB,QAAQ;IAC5C,iBAAiB,aAAa,kCAAkC;IAChE,YAAY,YAAY;;IAExB"}
|
package/dist/statewire.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { LIVE_CONNECTION, useStatewireCore } from "./client/core.js";
|
|
2
|
+
import { SessionStorageContext } from "./client/sessionStore.js";
|
|
2
3
|
import { useMemo, useRef, useState, useSyncExternalStore } from "@assistant-ui/tap/react-shim";
|
|
3
|
-
import { createTapRoot, flushTapSync, resource, useResource } from "@assistant-ui/tap";
|
|
4
|
+
import { createTapRoot, flushTapSync, resource, useContextProvider, useResource, useResources, withKey } from "@assistant-ui/tap";
|
|
4
5
|
//#region src/statewire.ts
|
|
6
|
+
const useSessionTransport = ({ transport, session }) => useContextProvider(SessionStorageContext, session, () => useResource(transport));
|
|
7
|
+
const SessionTransport = resource(useSessionTransport);
|
|
5
8
|
const useStatewire = (options) => {
|
|
6
9
|
const latest = useRef(options);
|
|
7
10
|
latest.current = options;
|
|
8
|
-
const
|
|
11
|
+
const session = useResources(options.storage?.session !== void 0 ? [withKey("session", options.storage.session)] : [])[0];
|
|
12
|
+
const value = useResource(SessionTransport({
|
|
13
|
+
transport: options.transport,
|
|
14
|
+
session
|
|
15
|
+
}));
|
|
9
16
|
const core = useStatewireCore(() => latest.current, value);
|
|
10
17
|
const [subscribe] = useState(() => (listener) => core.subscribe(() => flushTapSync(listener)));
|
|
11
18
|
const state = useSyncExternalStore(subscribe, core.getState, core.getState);
|