statewire 0.9.2 → 0.10.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 +7 -4
- package/dist/client/session.d.ts.map +1 -1
- package/dist/client/session.js +59 -11
- 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 +33 -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 +85 -15
- 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 +64 -8
- package/src/types.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.d.ts","names":[],"sources":["../../src/client/connection.ts"],"mappings":";;;;KAQY;EACV;EACA;;KAGU;EACV;EACA;EACA;EACA;EACA;EACA;;EAEA;;KAGU;EACV;EACA;EACA;EACA;EACA;EACA;;KAGU;EACV;EACA,MAAM;EACN;EACA;EACA,MAAM;EACN,MAAM,cAAc;;KAGV;EAAS;EAAkB;;KAoC3B;KAEA;EACV,YAAY,gBAAgB;EAC5B;EACA;EACA;EACA;EACA,SAAS;EACT;EACA,MAAM;EACN,MAAM;EACN,OAAO;EACP,UAAU;EACV,KAAK;EACL,WAAW,YAAY,gBAAgB;EACvC,cAAc,OAAO,oBAAoB,QAAQ;EACjD,SAAS,YAAY,gBAAgB;EACrC;;cA8CW,qBAAsB,mBAAiB;
|
|
1
|
+
{"version":3,"file":"connection.d.ts","names":[],"sources":["../../src/client/connection.ts"],"mappings":";;;;KAQY;EACV;EACA;;KAGU;EACV;EACA;EACA;EACA;EACA;EACA;;EAEA;;KAGU;EACV;EACA;EACA;EACA;EACA;EACA;;KAGU;EACV;EACA,MAAM;EACN;EACA;EACA,MAAM;EACN,MAAM,cAAc;;KAGV;EAAS;EAAkB;;KAoC3B;KAEA;EACV,YAAY,gBAAgB;EAC5B;EACA;EACA;EACA;EACA;EACA,SAAS;EACT;EACA,MAAM;EACN,MAAM;EACN,OAAO;EACP,UAAU;EACV,KAAK;EACL,WAAW,YAAY,gBAAgB;EACvC,cAAc,OAAO,oBAAoB,QAAQ;EACjD,SAAS,YAAY,gBAAgB;EACrC;;cA8CW,qBAAsB,mBAAiB;cAiOvC,iBACX,GAAG,MACH,mBACC;cAwFU,mBAAoB;EAC/B;EACA,aAAa,YAAY,gBAAgB;EACzC,KAAK;EACL,cAAc,GAAG,MAAM,OAAO,oBAAoB,QAAQ;MACxD;cAsDS,WAAY,SAAS,gBAAW"}
|
|
@@ -132,6 +132,7 @@ const handleHello = (c, env) => {
|
|
|
132
132
|
c.awaitingHello = false;
|
|
133
133
|
c.hasSnapshot = true;
|
|
134
134
|
c.liveAt = Date.now();
|
|
135
|
+
c.authDenials = 0;
|
|
135
136
|
c.episode = null;
|
|
136
137
|
setStatus(c, { status: "live" });
|
|
137
138
|
};
|
|
@@ -194,7 +195,9 @@ const settleFin = (c, fin) => {
|
|
|
194
195
|
setStatus(c, { status: "idle" });
|
|
195
196
|
return "idle";
|
|
196
197
|
}
|
|
197
|
-
const
|
|
198
|
+
const denial = finish.reason === "unauthorized";
|
|
199
|
+
const terminal = finish.reason === "gone" || finish.reason === "superseded" || denial && c.authDenials > 0;
|
|
200
|
+
if (denial) c.authDenials += 1;
|
|
198
201
|
c.ctx.onFinish(terminal ? finish : {
|
|
199
202
|
...finish,
|
|
200
203
|
reason: "error"
|
|
@@ -204,6 +207,7 @@ const settleFin = (c, fin) => {
|
|
|
204
207
|
cause: transientCauseOf(finish.reason),
|
|
205
208
|
...finish.message !== void 0 && { message: finish.message }
|
|
206
209
|
};
|
|
210
|
+
if (denial) openGate(c.gate);
|
|
207
211
|
return "reattach";
|
|
208
212
|
}
|
|
209
213
|
setStatus(c, {
|
|
@@ -248,6 +252,7 @@ const reconnectConn = (c) => {
|
|
|
248
252
|
if (!c.lifetime) return;
|
|
249
253
|
if (c.connection.status === "gone" || c.connection.status === "idle") {
|
|
250
254
|
c.attempt = 0;
|
|
255
|
+
c.authDenials = 0;
|
|
251
256
|
c.episode = { cause: "dropped" };
|
|
252
257
|
flushTapSync(() => {
|
|
253
258
|
scheduleRetry(c, void 0, true);
|
|
@@ -296,6 +301,7 @@ const useStatewireConn = (props) => {
|
|
|
296
301
|
hasSnapshot: false,
|
|
297
302
|
lease: null,
|
|
298
303
|
attempt: 0,
|
|
304
|
+
authDenials: 0,
|
|
299
305
|
liveAt: null,
|
|
300
306
|
episode: null,
|
|
301
307
|
awaitingHello: true,
|
|
@@ -321,6 +327,7 @@ const useStatewireConn = (props) => {
|
|
|
321
327
|
c.hasSnapshot = false;
|
|
322
328
|
c.lease = null;
|
|
323
329
|
c.attempt = 0;
|
|
330
|
+
c.authDenials = 0;
|
|
324
331
|
c.liveAt = null;
|
|
325
332
|
c.episode = null;
|
|
326
333
|
if (c.connection.status !== "connecting") setStatus(c, { status: "connecting" });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.js","names":["useEffect","useRef","useState","flushTapSync","reviveOps","reviveValue","backoff","timing","ConnectionState","StatewireRemoteProps","StatewireClient","StatewireHost","Episode","cause","message","StatewireCmd","seq","type","code","payload","meta","dur","StatewireSyn","ctxSeq","record","id","idle","retry","StatewireEnvelope","ops","cmd","next","syn","fin","Fin","Gate","latched","wake","newGate","undefined","takeGate","g","was","waitGate","timeoutMs","Promise","resolve","timer","setTimeout","clearTimeout","openGate","AttemptOutcome","Conn","connection","Connection","hasSnapshot","lease","attempt","liveAt","episode","awaitingHello","held","gate","abort","AbortController","lifetime","ctx","onChange","attemptOnce","notify","reconnect","setStatus","c","changed","status","protocolError","Error","isObject","value","Record","Array","isArray","isNonnegativeSafeInteger","Number","isSafeInteger","isPositiveSafeInteger","validateOptionalString","field","validateSynScalars","validateRefusalSyn","validateCmd","validateFin","validateEnvelope","entry","releaseHeld","length","onCmds","cmdsOf","env","snapshotOf","op","Object","hasOwn","helloSynOf","handleHello","onHello","onIdle","Date","now","warnedUnknownSynFact","warnUnknownSynFact","console","warn","deliverSyn","onRefusal","keys","deliverEnvelope","onOps","cmds","push","transientCauseOf","reason","settleFin","finish","rest","terminal","onFinish","handleEnvelope","scheduleRetry","failure","immediate","backoffResetMs","delay","nextRetryAt","lastError","reconnectConn","run","alive","outcome","error","signal","aborted","onDetach","useStatewireConn","props","url","latest","current","setConnection","conn","classify","timeout","AbortSignal"],"sources":["../../src/client/connection.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport { flushTapSync } from \"@assistant-ui/tap\";\nimport { reviveOps, reviveValue } from \"../wire/meta\";\nimport { backoff, timing } from \"./timing\";\nimport type { ConnectionState, StatewireRemoteProps } from \"../transports/sse\";\nimport type { StatewireClient } from \"../statewire\";\nimport type { StatewireHost } from \"../host\";\n\nexport type Episode = {\n cause: \"dropped\" | \"evicted\" | \"error\" | \"unauthorized\";\n message?: string;\n};\n\nexport type StatewireCmd = {\n seq: number;\n type?: \"applied\" | \"result\" | \"rejected\" | \"failed\" | \"crashed\" | \"expired\";\n code?: string;\n message?: string;\n payload?: unknown;\n meta?: unknown;\n /** Cumulative: resending through this seq will never be needed. */\n dur?: boolean;\n};\n\nexport type StatewireSyn = {\n seq?: number;\n ctxSeq?: number;\n record?: \"fresh\" | \"resumed\" | \"ephemeral\";\n id?: string;\n idle?: boolean;\n retry?: number;\n};\n\nexport type StatewireEnvelope = {\n ops?: unknown[];\n cmd?: StatewireCmd[];\n next?: true;\n idle?: boolean;\n syn?: StatewireSyn;\n fin?: StatewireHost.Fin;\n};\n\nexport type Gate = { latched: boolean; wake: (() => void) | undefined };\n\nconst newGate = (): Gate => ({ latched: false, wake: undefined });\n\nconst takeGate = (g: Gate) => {\n const was = g.latched;\n g.latched = false;\n return was;\n};\n\nconst waitGate = (g: Gate, timeoutMs?: number) => {\n if (g.latched) {\n g.latched = false;\n return Promise.resolve();\n }\n return new Promise<void>((resolve) => {\n const timer =\n timeoutMs === undefined\n ? undefined\n : setTimeout(() => {\n g.wake = undefined;\n resolve();\n }, timeoutMs);\n g.wake = () => {\n clearTimeout(timer);\n g.wake = undefined;\n resolve();\n };\n });\n};\n\nconst openGate = (g: Gate) => {\n if (g.wake) g.wake();\n else g.latched = true;\n};\n\nexport type AttemptOutcome = \"stop\" | \"reattach\" | \"ended\" | \"idle\";\n\nexport type Conn = {\n connection: StatewireClient.Connection;\n hasSnapshot: boolean;\n lease: string | null;\n attempt: number;\n liveAt: number | null;\n episode: Episode | null;\n awaitingHello: boolean;\n held: StatewireCmd[];\n gate: Gate;\n abort: AbortController | null;\n lifetime: AbortController | null;\n ctx: StatewireRemoteProps;\n onChange: (connection: StatewireClient.Connection) => void;\n attemptOnce: (abort: AbortController) => Promise<AttemptOutcome>;\n notify: (connection: StatewireClient.Connection) => void;\n reconnect: () => void;\n};\n\nconst setStatus = (c: Conn, next: ConnectionState) => {\n const changed = next.status !== c.connection.status;\n c.connection = { ...next, reconnect: c.reconnect };\n c.notify(c.connection);\n if (changed) c.onChange(c.connection);\n};\n\nconst protocolError = (message: string): never => {\n throw new Error(`statewire: protocol error: ${message}`);\n};\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isNonnegativeSafeInteger = (value: unknown): value is number =>\n Number.isSafeInteger(value) && (value as number) >= 0;\n\nconst isPositiveSafeInteger = (value: unknown): value is number =>\n Number.isSafeInteger(value) && (value as number) > 0;\n\nconst validateOptionalString = (value: unknown, field: string): void => {\n if (value !== undefined && typeof value !== \"string\") {\n protocolError(`${field} must be a string`);\n }\n};\n\nconst validateSynScalars = (syn: Record<string, unknown>) => {\n if (syn[\"seq\"] !== undefined && !isNonnegativeSafeInteger(syn[\"seq\"])) {\n protocolError(\"syn.seq must be a nonnegative safe integer\");\n }\n if (syn[\"ctxSeq\"] !== undefined && !isNonnegativeSafeInteger(syn[\"ctxSeq\"])) {\n protocolError(\"syn.ctxSeq must be a nonnegative safe integer\");\n }\n if (syn[\"retry\"] !== undefined && !isNonnegativeSafeInteger(syn[\"retry\"])) {\n protocolError(\"syn.retry must be a nonnegative safe integer\");\n }\n validateOptionalString(syn[\"id\"], \"syn.id\");\n if (syn[\"idle\"] !== undefined && typeof syn[\"idle\"] !== \"boolean\") {\n protocolError(\"syn.idle must be a boolean\");\n }\n validateOptionalString(syn[\"record\"], \"syn.record\");\n};\n\nexport const validateRefusalSyn = (value: unknown): StatewireSyn => {\n if (!isObject(value)) return protocolError(\"syn must be an object\");\n validateSynScalars(value);\n if (\n value[\"id\"] !== undefined ||\n value[\"idle\"] !== undefined ||\n value[\"record\"] !== undefined\n ) {\n protocolError(\"invalid refusal syn\");\n }\n if (\n value[\"seq\"] === undefined &&\n value[\"ctxSeq\"] === undefined &&\n value[\"retry\"] === undefined\n ) {\n protocolError(\"refusal syn has no watermarks or retry\");\n }\n return value as StatewireSyn;\n};\n\nconst validateCmd = (value: unknown): value is StatewireCmd => {\n if (!isObject(value)) return protocolError(\"cmd entry must be an object\");\n if (!isPositiveSafeInteger(value[\"seq\"])) {\n protocolError(\"cmd.seq must be a positive safe integer\");\n }\n validateOptionalString(value[\"type\"], \"cmd.type\");\n validateOptionalString(value[\"code\"], \"cmd.code\");\n validateOptionalString(value[\"message\"], \"cmd.message\");\n if (value[\"dur\"] !== undefined && value[\"dur\"] !== true) {\n protocolError(\"cmd.dur must be true\");\n }\n return true;\n};\n\nconst validateFin = (value: unknown): value is StatewireHost.Fin => {\n if (!isObject(value)) return protocolError(\"fin must be an object\");\n if (typeof value[\"reason\"] !== \"string\") {\n protocolError(\"fin.reason must be a string\");\n }\n validateOptionalString(value[\"message\"], \"fin.message\");\n return true;\n};\n\nconst validateEnvelope = (value: unknown): StatewireEnvelope => {\n if (!isObject(value)) return protocolError(\"packet must be an object\");\n if (value[\"ops\"] !== undefined && !Array.isArray(value[\"ops\"])) {\n protocolError(\"ops must be an array\");\n }\n if (value[\"cmd\"] !== undefined) {\n const cmd = value[\"cmd\"];\n if (!Array.isArray(cmd)) return protocolError(\"cmd must be an array\");\n for (const entry of cmd) validateCmd(entry);\n }\n if (value[\"idle\"] !== undefined && typeof value[\"idle\"] !== \"boolean\") {\n protocolError(\"idle must be a boolean\");\n }\n if (value[\"next\"] !== undefined) {\n if (value[\"next\"] !== true) protocolError(\"next must be true\");\n if (\n value[\"cmd\"] === undefined ||\n value[\"ops\"] !== undefined ||\n value[\"fin\"] !== undefined\n ) {\n protocolError(\"next must ride a cmd-only packet\");\n }\n }\n if (value[\"fin\"] !== undefined) validateFin(value[\"fin\"]);\n return value as StatewireEnvelope;\n};\n\n// A hold's release delivers, never settles: fin and bare close both drain it.\nconst releaseHeld = (c: Conn) => {\n if (c.held.length === 0) return;\n const held = c.held;\n c.held = [];\n c.ctx.onCmds(held);\n};\n\nconst cmdsOf = (env: StatewireEnvelope): StatewireCmd[] => {\n if (env.cmd === undefined) return [];\n return env.cmd;\n};\n\nconst snapshotOf = (env: StatewireEnvelope): unknown[] => {\n const ops = env.ops;\n if (ops === undefined) return protocolError(\"hello missing snapshot\");\n if (ops.length !== 1 || !isObject(ops[0])) {\n return protocolError(\"hello snapshot must be one root replace op\");\n }\n const op = ops[0];\n if (\n op[\"op\"] !== \"replace\" ||\n !Array.isArray(op[\"path\"]) ||\n op[\"path\"].length !== 0 ||\n !Object.hasOwn(op, \"value\")\n ) {\n return protocolError(\"hello snapshot must be one root replace op\");\n }\n return reviveOps(ops);\n};\n\nconst helloSynOf = (env: StatewireEnvelope): StatewireSyn => {\n if (!isObject(env.syn)) return protocolError(\"expected hello\");\n validateSynScalars(env.syn);\n if (env.syn[\"retry\"] !== undefined) {\n protocolError(\"hello syn cannot carry retry\");\n }\n return env.syn as StatewireSyn;\n};\n\nconst handleHello = (c: Conn, env: StatewireEnvelope) => {\n const syn = helloSynOf(env);\n const ops = snapshotOf(env);\n c.ctx.onHello({\n ops,\n syn,\n });\n c.ctx.onIdle(syn.idle ?? false);\n c.awaitingHello = false;\n c.hasSnapshot = true;\n c.liveAt = Date.now();\n c.episode = null;\n setStatus(c, { status: \"live\" });\n};\n\nlet warnedUnknownSynFact = false;\n\nconst warnUnknownSynFact = () => {\n if (warnedUnknownSynFact) return;\n warnedUnknownSynFact = true;\n console.warn(\"statewire: ignoring unknown syn fact\");\n};\n\nconst deliverSyn = (c: Conn, value: unknown) => {\n if (!isObject(value)) return protocolError(\"syn must be an object\");\n validateSynScalars(value);\n if (value[\"record\"] !== undefined || value[\"idle\"] !== undefined) {\n protocolError(\"invalid mid-stream syn\");\n }\n if (\n value[\"seq\"] !== undefined ||\n value[\"ctxSeq\"] !== undefined ||\n value[\"retry\"] !== undefined\n ) {\n c.ctx.onRefusal(validateRefusalSyn(value));\n return;\n }\n if (value[\"id\"] !== undefined) return;\n if (Object.keys(value).length === 0) {\n protocolError(\"invalid mid-stream syn\");\n }\n warnUnknownSynFact();\n};\n\nconst deliverEnvelope = (c: Conn, env: StatewireEnvelope) => {\n if (env.syn !== undefined) deliverSyn(c, env.syn);\n if (env.ops !== undefined) {\n const held = c.held;\n c.held = [];\n c.ctx.onOps({\n ops: reviveOps(env.ops),\n cmds: [...held, ...cmdsOf(env)],\n });\n } else if (env.cmd !== undefined) {\n const cmds = cmdsOf(env);\n if (env.next === true) c.held.push(...cmds);\n else c.ctx.onCmds(cmds);\n }\n if (typeof env.idle === \"boolean\") c.ctx.onIdle(env.idle);\n};\n\nconst transientCauseOf = (\n reason: StatewireHost.Fin[\"reason\"],\n): Episode[\"cause\"] => {\n switch (reason) {\n case \"evicted\":\n return \"evicted\";\n case \"error\":\n return \"error\";\n case \"unauthorized\":\n return \"unauthorized\";\n default:\n return \"error\";\n }\n};\n\nconst settleFin = (c: Conn, fin: StatewireHost.Fin): AttemptOutcome => {\n let finish = fin;\n if (finish.meta !== undefined) {\n const { meta, ...rest } = finish;\n finish = { ...rest, payload: reviveValue(rest.payload, meta) };\n }\n releaseHeld(c);\n if (finish.reason === \"idle\") {\n c.ctx.onIdle(true);\n c.attempt = 0;\n c.episode = null;\n setStatus(c, { status: \"idle\" });\n return \"idle\";\n }\n const terminal = finish.reason === \"gone\" || finish.reason === \"superseded\";\n c.ctx.onFinish(terminal ? finish : { ...finish, reason: \"error\" });\n if (!terminal) {\n c.episode = {\n cause: transientCauseOf(finish.reason),\n ...(finish.message !== undefined && { message: finish.message }),\n };\n return \"reattach\";\n }\n setStatus(c, {\n status: \"gone\",\n ...(finish.message !== undefined && { message: finish.message }),\n ...(finish.payload !== undefined && { payload: finish.payload }),\n });\n return \"stop\";\n};\n\nexport const handleEnvelope = (\n c: Conn,\n value: unknown,\n): AttemptOutcome | null => {\n const env = validateEnvelope(value);\n if (c.awaitingHello) {\n if (env.fin !== undefined) return settleFin(c, env.fin);\n handleHello(c, env);\n return null;\n }\n deliverEnvelope(c, env);\n if (env.fin !== undefined) return settleFin(c, env.fin);\n if (c.connection.status !== \"live\") {\n c.attempt = 0;\n c.episode = null;\n setStatus(c, { status: \"live\" });\n }\n return null;\n};\n\nconst scheduleRetry = (\n c: Conn,\n failure: string | undefined,\n immediate: boolean,\n) => {\n if (c.liveAt !== null && Date.now() - c.liveAt >= timing.backoffResetMs) {\n c.attempt = 0;\n }\n c.liveAt = null;\n c.attempt += 1;\n c.episode ??= { cause: \"dropped\" };\n const delay = immediate ? 0 : backoff(c.attempt);\n setStatus(c, {\n status: \"reconnecting\",\n ...c.episode,\n attempt: c.attempt,\n nextRetryAt: Date.now() + delay,\n ...(failure !== undefined && { lastError: failure }),\n });\n return delay;\n};\n\nconst reconnectConn = (c: Conn) => {\n if (!c.lifetime) return;\n if (c.connection.status === \"gone\" || c.connection.status === \"idle\") {\n c.attempt = 0;\n c.episode = { cause: \"dropped\" };\n flushTapSync(() => {\n scheduleRetry(c, undefined, true);\n });\n }\n openGate(c.gate);\n c.abort?.abort();\n};\n\nconst run = async (c: Conn, lifetime: AbortController, gate: Gate) => {\n const alive = () => c.lifetime === lifetime;\n while (alive()) {\n const abort = new AbortController();\n c.abort = abort;\n c.awaitingHello = true;\n c.held = [];\n let outcome: AttemptOutcome | undefined;\n let failure: string | undefined;\n try {\n outcome = await c.attemptOnce(abort);\n } catch (error) {\n if (!abort.signal.aborted) {\n failure =\n error instanceof Error && error.message ? error.message : \"network\";\n }\n }\n abort.abort();\n if (!alive()) return;\n flushTapSync(() => {\n releaseHeld(c);\n c.ctx.onDetach();\n });\n if (!alive()) return;\n if (outcome === \"stop\" || outcome === \"idle\") {\n await waitGate(gate);\n continue;\n }\n const immediate = takeGate(gate);\n const delay = flushTapSync(() => scheduleRetry(c, failure, immediate));\n if (delay === 0) continue;\n await waitGate(gate, delay);\n }\n};\n\nexport const useStatewireConn = (props: {\n url: string;\n onChange?: ((connection: StatewireClient.Connection) => void) | undefined;\n ctx: StatewireRemoteProps;\n attemptOnce: (c: Conn, abort: AbortController) => Promise<AttemptOutcome>;\n}): Conn => {\n const latest = useRef(props);\n latest.current = props;\n const [, setConnection] = useState<StatewireClient.Connection | null>(null);\n const [c] = useState((): Conn => {\n const conn: Conn = {\n hasSnapshot: false,\n lease: null,\n attempt: 0,\n liveAt: null,\n episode: null,\n awaitingHello: true,\n held: [],\n abort: null,\n gate: newGate(),\n lifetime: null,\n get ctx() {\n return latest.current.ctx;\n },\n onChange: (connection) => latest.current.onChange?.(connection),\n attemptOnce: (abort) => latest.current.attemptOnce(conn, abort),\n notify: setConnection,\n reconnect: () => reconnectConn(conn),\n connection: { status: \"connecting\", reconnect: () => conn.reconnect() },\n };\n return conn;\n });\n\n useEffect(() => {\n c.hasSnapshot = false;\n c.lease = null;\n c.attempt = 0;\n c.liveAt = null;\n c.episode = null;\n if (c.connection.status !== \"connecting\") {\n setStatus(c, { status: \"connecting\" });\n }\n const lifetime = new AbortController();\n c.lifetime = lifetime;\n c.gate = newGate();\n void run(c, lifetime, c.gate);\n return () => {\n c.abort?.abort();\n lifetime.abort();\n if (c.lifetime === lifetime) c.lifetime = null;\n openGate(c.gate);\n };\n }, [c, props.url]);\n\n return c;\n};\n\nexport const classify = (timeout: AbortSignal) =>\n new Error(timeout.aborted ? \"timeout\" : \"network\");\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"connection.js","names":["useEffect","useRef","useState","flushTapSync","reviveOps","reviveValue","backoff","timing","ConnectionState","StatewireRemoteProps","StatewireClient","StatewireHost","Episode","cause","message","StatewireCmd","seq","type","code","payload","meta","dur","StatewireSyn","ctxSeq","record","id","idle","retry","StatewireEnvelope","ops","cmd","next","syn","fin","Fin","Gate","latched","wake","newGate","undefined","takeGate","g","was","waitGate","timeoutMs","Promise","resolve","timer","setTimeout","clearTimeout","openGate","AttemptOutcome","Conn","connection","Connection","hasSnapshot","lease","attempt","authDenials","liveAt","episode","awaitingHello","held","gate","abort","AbortController","lifetime","ctx","onChange","attemptOnce","notify","reconnect","setStatus","c","changed","status","protocolError","Error","isObject","value","Record","Array","isArray","isNonnegativeSafeInteger","Number","isSafeInteger","isPositiveSafeInteger","validateOptionalString","field","validateSynScalars","validateRefusalSyn","validateCmd","validateFin","validateEnvelope","entry","releaseHeld","length","onCmds","cmdsOf","env","snapshotOf","op","Object","hasOwn","helloSynOf","handleHello","onHello","onIdle","Date","now","warnedUnknownSynFact","warnUnknownSynFact","console","warn","deliverSyn","onRefusal","keys","deliverEnvelope","onOps","cmds","push","transientCauseOf","reason","settleFin","finish","rest","denial","terminal","onFinish","handleEnvelope","scheduleRetry","failure","immediate","backoffResetMs","delay","nextRetryAt","lastError","reconnectConn","run","alive","outcome","error","signal","aborted","onDetach","useStatewireConn","props","url","latest","current","setConnection","conn","classify","timeout","AbortSignal"],"sources":["../../src/client/connection.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport { flushTapSync } from \"@assistant-ui/tap\";\nimport { reviveOps, reviveValue } from \"../wire/meta\";\nimport { backoff, timing } from \"./timing\";\nimport type { ConnectionState, StatewireRemoteProps } from \"../transports/sse\";\nimport type { StatewireClient } from \"../statewire\";\nimport type { StatewireHost } from \"../host\";\n\nexport type Episode = {\n cause: \"dropped\" | \"evicted\" | \"error\" | \"unauthorized\";\n message?: string;\n};\n\nexport type StatewireCmd = {\n seq: number;\n type?: \"applied\" | \"result\" | \"rejected\" | \"failed\" | \"crashed\" | \"expired\";\n code?: string;\n message?: string;\n payload?: unknown;\n meta?: unknown;\n /** Cumulative: resending through this seq will never be needed. */\n dur?: boolean;\n};\n\nexport type StatewireSyn = {\n seq?: number;\n ctxSeq?: number;\n record?: \"fresh\" | \"resumed\" | \"ephemeral\";\n id?: string;\n idle?: boolean;\n retry?: number;\n};\n\nexport type StatewireEnvelope = {\n ops?: unknown[];\n cmd?: StatewireCmd[];\n next?: true;\n idle?: boolean;\n syn?: StatewireSyn;\n fin?: StatewireHost.Fin;\n};\n\nexport type Gate = { latched: boolean; wake: (() => void) | undefined };\n\nconst newGate = (): Gate => ({ latched: false, wake: undefined });\n\nconst takeGate = (g: Gate) => {\n const was = g.latched;\n g.latched = false;\n return was;\n};\n\nconst waitGate = (g: Gate, timeoutMs?: number) => {\n if (g.latched) {\n g.latched = false;\n return Promise.resolve();\n }\n return new Promise<void>((resolve) => {\n const timer =\n timeoutMs === undefined\n ? undefined\n : setTimeout(() => {\n g.wake = undefined;\n resolve();\n }, timeoutMs);\n g.wake = () => {\n clearTimeout(timer);\n g.wake = undefined;\n resolve();\n };\n });\n};\n\nconst openGate = (g: Gate) => {\n if (g.wake) g.wake();\n else g.latched = true;\n};\n\nexport type AttemptOutcome = \"stop\" | \"reattach\" | \"ended\" | \"idle\";\n\nexport type Conn = {\n connection: StatewireClient.Connection;\n hasSnapshot: boolean;\n lease: string | null;\n attempt: number;\n authDenials: number;\n liveAt: number | null;\n episode: Episode | null;\n awaitingHello: boolean;\n held: StatewireCmd[];\n gate: Gate;\n abort: AbortController | null;\n lifetime: AbortController | null;\n ctx: StatewireRemoteProps;\n onChange: (connection: StatewireClient.Connection) => void;\n attemptOnce: (abort: AbortController) => Promise<AttemptOutcome>;\n notify: (connection: StatewireClient.Connection) => void;\n reconnect: () => void;\n};\n\nconst setStatus = (c: Conn, next: ConnectionState) => {\n const changed = next.status !== c.connection.status;\n c.connection = { ...next, reconnect: c.reconnect };\n c.notify(c.connection);\n if (changed) c.onChange(c.connection);\n};\n\nconst protocolError = (message: string): never => {\n throw new Error(`statewire: protocol error: ${message}`);\n};\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isNonnegativeSafeInteger = (value: unknown): value is number =>\n Number.isSafeInteger(value) && (value as number) >= 0;\n\nconst isPositiveSafeInteger = (value: unknown): value is number =>\n Number.isSafeInteger(value) && (value as number) > 0;\n\nconst validateOptionalString = (value: unknown, field: string): void => {\n if (value !== undefined && typeof value !== \"string\") {\n protocolError(`${field} must be a string`);\n }\n};\n\nconst validateSynScalars = (syn: Record<string, unknown>) => {\n if (syn[\"seq\"] !== undefined && !isNonnegativeSafeInteger(syn[\"seq\"])) {\n protocolError(\"syn.seq must be a nonnegative safe integer\");\n }\n if (syn[\"ctxSeq\"] !== undefined && !isNonnegativeSafeInteger(syn[\"ctxSeq\"])) {\n protocolError(\"syn.ctxSeq must be a nonnegative safe integer\");\n }\n if (syn[\"retry\"] !== undefined && !isNonnegativeSafeInteger(syn[\"retry\"])) {\n protocolError(\"syn.retry must be a nonnegative safe integer\");\n }\n validateOptionalString(syn[\"id\"], \"syn.id\");\n if (syn[\"idle\"] !== undefined && typeof syn[\"idle\"] !== \"boolean\") {\n protocolError(\"syn.idle must be a boolean\");\n }\n validateOptionalString(syn[\"record\"], \"syn.record\");\n};\n\nexport const validateRefusalSyn = (value: unknown): StatewireSyn => {\n if (!isObject(value)) return protocolError(\"syn must be an object\");\n validateSynScalars(value);\n if (\n value[\"id\"] !== undefined ||\n value[\"idle\"] !== undefined ||\n value[\"record\"] !== undefined\n ) {\n protocolError(\"invalid refusal syn\");\n }\n if (\n value[\"seq\"] === undefined &&\n value[\"ctxSeq\"] === undefined &&\n value[\"retry\"] === undefined\n ) {\n protocolError(\"refusal syn has no watermarks or retry\");\n }\n return value as StatewireSyn;\n};\n\nconst validateCmd = (value: unknown): value is StatewireCmd => {\n if (!isObject(value)) return protocolError(\"cmd entry must be an object\");\n if (!isPositiveSafeInteger(value[\"seq\"])) {\n protocolError(\"cmd.seq must be a positive safe integer\");\n }\n validateOptionalString(value[\"type\"], \"cmd.type\");\n validateOptionalString(value[\"code\"], \"cmd.code\");\n validateOptionalString(value[\"message\"], \"cmd.message\");\n if (value[\"dur\"] !== undefined && value[\"dur\"] !== true) {\n protocolError(\"cmd.dur must be true\");\n }\n return true;\n};\n\nconst validateFin = (value: unknown): value is StatewireHost.Fin => {\n if (!isObject(value)) return protocolError(\"fin must be an object\");\n if (typeof value[\"reason\"] !== \"string\") {\n protocolError(\"fin.reason must be a string\");\n }\n validateOptionalString(value[\"message\"], \"fin.message\");\n return true;\n};\n\nconst validateEnvelope = (value: unknown): StatewireEnvelope => {\n if (!isObject(value)) return protocolError(\"packet must be an object\");\n if (value[\"ops\"] !== undefined && !Array.isArray(value[\"ops\"])) {\n protocolError(\"ops must be an array\");\n }\n if (value[\"cmd\"] !== undefined) {\n const cmd = value[\"cmd\"];\n if (!Array.isArray(cmd)) return protocolError(\"cmd must be an array\");\n for (const entry of cmd) validateCmd(entry);\n }\n if (value[\"idle\"] !== undefined && typeof value[\"idle\"] !== \"boolean\") {\n protocolError(\"idle must be a boolean\");\n }\n if (value[\"next\"] !== undefined) {\n if (value[\"next\"] !== true) protocolError(\"next must be true\");\n if (\n value[\"cmd\"] === undefined ||\n value[\"ops\"] !== undefined ||\n value[\"fin\"] !== undefined\n ) {\n protocolError(\"next must ride a cmd-only packet\");\n }\n }\n if (value[\"fin\"] !== undefined) validateFin(value[\"fin\"]);\n return value as StatewireEnvelope;\n};\n\n// A hold's release delivers, never settles: fin and bare close both drain it.\nconst releaseHeld = (c: Conn) => {\n if (c.held.length === 0) return;\n const held = c.held;\n c.held = [];\n c.ctx.onCmds(held);\n};\n\nconst cmdsOf = (env: StatewireEnvelope): StatewireCmd[] => {\n if (env.cmd === undefined) return [];\n return env.cmd;\n};\n\nconst snapshotOf = (env: StatewireEnvelope): unknown[] => {\n const ops = env.ops;\n if (ops === undefined) return protocolError(\"hello missing snapshot\");\n if (ops.length !== 1 || !isObject(ops[0])) {\n return protocolError(\"hello snapshot must be one root replace op\");\n }\n const op = ops[0];\n if (\n op[\"op\"] !== \"replace\" ||\n !Array.isArray(op[\"path\"]) ||\n op[\"path\"].length !== 0 ||\n !Object.hasOwn(op, \"value\")\n ) {\n return protocolError(\"hello snapshot must be one root replace op\");\n }\n return reviveOps(ops);\n};\n\nconst helloSynOf = (env: StatewireEnvelope): StatewireSyn => {\n if (!isObject(env.syn)) return protocolError(\"expected hello\");\n validateSynScalars(env.syn);\n if (env.syn[\"retry\"] !== undefined) {\n protocolError(\"hello syn cannot carry retry\");\n }\n return env.syn as StatewireSyn;\n};\n\nconst handleHello = (c: Conn, env: StatewireEnvelope) => {\n const syn = helloSynOf(env);\n const ops = snapshotOf(env);\n c.ctx.onHello({\n ops,\n syn,\n });\n c.ctx.onIdle(syn.idle ?? false);\n c.awaitingHello = false;\n c.hasSnapshot = true;\n c.liveAt = Date.now();\n c.authDenials = 0;\n c.episode = null;\n setStatus(c, { status: \"live\" });\n};\n\nlet warnedUnknownSynFact = false;\n\nconst warnUnknownSynFact = () => {\n if (warnedUnknownSynFact) return;\n warnedUnknownSynFact = true;\n console.warn(\"statewire: ignoring unknown syn fact\");\n};\n\nconst deliverSyn = (c: Conn, value: unknown) => {\n if (!isObject(value)) return protocolError(\"syn must be an object\");\n validateSynScalars(value);\n if (value[\"record\"] !== undefined || value[\"idle\"] !== undefined) {\n protocolError(\"invalid mid-stream syn\");\n }\n if (\n value[\"seq\"] !== undefined ||\n value[\"ctxSeq\"] !== undefined ||\n value[\"retry\"] !== undefined\n ) {\n c.ctx.onRefusal(validateRefusalSyn(value));\n return;\n }\n if (value[\"id\"] !== undefined) return;\n if (Object.keys(value).length === 0) {\n protocolError(\"invalid mid-stream syn\");\n }\n warnUnknownSynFact();\n};\n\nconst deliverEnvelope = (c: Conn, env: StatewireEnvelope) => {\n if (env.syn !== undefined) deliverSyn(c, env.syn);\n if (env.ops !== undefined) {\n const held = c.held;\n c.held = [];\n c.ctx.onOps({\n ops: reviveOps(env.ops),\n cmds: [...held, ...cmdsOf(env)],\n });\n } else if (env.cmd !== undefined) {\n const cmds = cmdsOf(env);\n if (env.next === true) c.held.push(...cmds);\n else c.ctx.onCmds(cmds);\n }\n if (typeof env.idle === \"boolean\") c.ctx.onIdle(env.idle);\n};\n\nconst transientCauseOf = (\n reason: StatewireHost.Fin[\"reason\"],\n): Episode[\"cause\"] => {\n switch (reason) {\n case \"evicted\":\n return \"evicted\";\n case \"error\":\n return \"error\";\n case \"unauthorized\":\n return \"unauthorized\";\n default:\n return \"error\";\n }\n};\n\nconst settleFin = (c: Conn, fin: StatewireHost.Fin): AttemptOutcome => {\n let finish = fin;\n if (finish.meta !== undefined) {\n const { meta, ...rest } = finish;\n finish = { ...rest, payload: reviveValue(rest.payload, meta) };\n }\n releaseHeld(c);\n if (finish.reason === \"idle\") {\n c.ctx.onIdle(true);\n c.attempt = 0;\n c.episode = null;\n setStatus(c, { status: \"idle\" });\n return \"idle\";\n }\n // An auth denial gets one immediate redial; a consecutive repeat parks as gone.\n const denial = finish.reason === \"unauthorized\";\n const terminal =\n finish.reason === \"gone\" ||\n finish.reason === \"superseded\" ||\n (denial && c.authDenials > 0);\n if (denial) c.authDenials += 1;\n c.ctx.onFinish(terminal ? finish : { ...finish, reason: \"error\" });\n if (!terminal) {\n c.episode = {\n cause: transientCauseOf(finish.reason),\n ...(finish.message !== undefined && { message: finish.message }),\n };\n if (denial) openGate(c.gate);\n return \"reattach\";\n }\n setStatus(c, {\n status: \"gone\",\n ...(finish.message !== undefined && { message: finish.message }),\n ...(finish.payload !== undefined && { payload: finish.payload }),\n });\n return \"stop\";\n};\n\nexport const handleEnvelope = (\n c: Conn,\n value: unknown,\n): AttemptOutcome | null => {\n const env = validateEnvelope(value);\n if (c.awaitingHello) {\n if (env.fin !== undefined) return settleFin(c, env.fin);\n handleHello(c, env);\n return null;\n }\n deliverEnvelope(c, env);\n if (env.fin !== undefined) return settleFin(c, env.fin);\n if (c.connection.status !== \"live\") {\n c.attempt = 0;\n c.episode = null;\n setStatus(c, { status: \"live\" });\n }\n return null;\n};\n\nconst scheduleRetry = (\n c: Conn,\n failure: string | undefined,\n immediate: boolean,\n) => {\n if (c.liveAt !== null && Date.now() - c.liveAt >= timing.backoffResetMs) {\n c.attempt = 0;\n }\n c.liveAt = null;\n c.attempt += 1;\n c.episode ??= { cause: \"dropped\" };\n const delay = immediate ? 0 : backoff(c.attempt);\n setStatus(c, {\n status: \"reconnecting\",\n ...c.episode,\n attempt: c.attempt,\n nextRetryAt: Date.now() + delay,\n ...(failure !== undefined && { lastError: failure }),\n });\n return delay;\n};\n\nconst reconnectConn = (c: Conn) => {\n if (!c.lifetime) return;\n if (c.connection.status === \"gone\" || c.connection.status === \"idle\") {\n c.attempt = 0;\n c.authDenials = 0;\n c.episode = { cause: \"dropped\" };\n flushTapSync(() => {\n scheduleRetry(c, undefined, true);\n });\n }\n openGate(c.gate);\n c.abort?.abort();\n};\n\nconst run = async (c: Conn, lifetime: AbortController, gate: Gate) => {\n const alive = () => c.lifetime === lifetime;\n while (alive()) {\n const abort = new AbortController();\n c.abort = abort;\n c.awaitingHello = true;\n c.held = [];\n let outcome: AttemptOutcome | undefined;\n let failure: string | undefined;\n try {\n outcome = await c.attemptOnce(abort);\n } catch (error) {\n if (!abort.signal.aborted) {\n failure =\n error instanceof Error && error.message ? error.message : \"network\";\n }\n }\n abort.abort();\n if (!alive()) return;\n flushTapSync(() => {\n releaseHeld(c);\n c.ctx.onDetach();\n });\n if (!alive()) return;\n if (outcome === \"stop\" || outcome === \"idle\") {\n await waitGate(gate);\n continue;\n }\n const immediate = takeGate(gate);\n const delay = flushTapSync(() => scheduleRetry(c, failure, immediate));\n if (delay === 0) continue;\n await waitGate(gate, delay);\n }\n};\n\nexport const useStatewireConn = (props: {\n url: string;\n onChange?: ((connection: StatewireClient.Connection) => void) | undefined;\n ctx: StatewireRemoteProps;\n attemptOnce: (c: Conn, abort: AbortController) => Promise<AttemptOutcome>;\n}): Conn => {\n const latest = useRef(props);\n latest.current = props;\n const [, setConnection] = useState<StatewireClient.Connection | null>(null);\n const [c] = useState((): Conn => {\n const conn: Conn = {\n hasSnapshot: false,\n lease: null,\n attempt: 0,\n authDenials: 0,\n liveAt: null,\n episode: null,\n awaitingHello: true,\n held: [],\n abort: null,\n gate: newGate(),\n lifetime: null,\n get ctx() {\n return latest.current.ctx;\n },\n onChange: (connection) => latest.current.onChange?.(connection),\n attemptOnce: (abort) => latest.current.attemptOnce(conn, abort),\n notify: setConnection,\n reconnect: () => reconnectConn(conn),\n connection: { status: \"connecting\", reconnect: () => conn.reconnect() },\n };\n return conn;\n });\n\n useEffect(() => {\n c.hasSnapshot = false;\n c.lease = null;\n c.attempt = 0;\n c.authDenials = 0;\n c.liveAt = null;\n c.episode = null;\n if (c.connection.status !== \"connecting\") {\n setStatus(c, { status: \"connecting\" });\n }\n const lifetime = new AbortController();\n c.lifetime = lifetime;\n c.gate = newGate();\n void run(c, lifetime, c.gate);\n return () => {\n c.abort?.abort();\n lifetime.abort();\n if (c.lifetime === lifetime) c.lifetime = null;\n openGate(c.gate);\n };\n }, [c, props.url]);\n\n return c;\n};\n\nexport const classify = (timeout: AbortSignal) =>\n new Error(timeout.aborted ? \"timeout\" : \"network\");\n"],"mappings":"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"}
|
package/dist/client/session.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StatewireClient } from "../statewire.js";
|
|
2
2
|
import { Statewire, StatewireSendError } from "../types.js";
|
|
3
3
|
import { StatewirePath } from "../wire/ops.js";
|
|
4
4
|
import { CommandsInvocation, CommandsListener } from "../host/host.js";
|
|
@@ -28,15 +28,18 @@ type CtxPending = {
|
|
|
28
28
|
posted: boolean;
|
|
29
29
|
};
|
|
30
30
|
type AttachState = "detached" | "attached" | "finished";
|
|
31
|
-
type StatewireSessionOptions =
|
|
31
|
+
type StatewireSessionOptions = {
|
|
32
|
+
sessionStore?: StatewireClient.SessionStorage | undefined;
|
|
32
33
|
onDone?: (() => void) | undefined;
|
|
33
34
|
};
|
|
34
35
|
type Session = {
|
|
35
|
-
record:
|
|
36
|
+
record: StatewireClient.SessionRecord;
|
|
36
37
|
ctx: StatewireRemoteProps;
|
|
37
38
|
recordPersisted: boolean;
|
|
38
|
-
store:
|
|
39
|
+
store: StatewireClient.SessionStorage;
|
|
39
40
|
ephemeral: boolean;
|
|
41
|
+
/** An ephemeral record died with its attach: the storage is off for good. */
|
|
42
|
+
deactivated: boolean;
|
|
40
43
|
sessionKnown: boolean;
|
|
41
44
|
durableSeq: number;
|
|
42
45
|
resumeAt: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","names":[],"sources":["../../src/client/session.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"session.d.ts","names":[],"sources":["../../src/client/session.ts"],"mappings":";;;;;;KA4BK;EACH;EACA;EACA;;KAGG;WACM;WACA,SAAS;WACT,KAAK;;EAEd;;EAEA;;EAEA;EACA,UAAU;EACV,SAAS,OAAO;;KAGb;WACM;WACA;EACT;;KAiBG;KAEO;EACV,eAAe,gBAAgB;EAC/B;;KAGG;EACH,QAAQ,gBAAgB;EACxB,KAAK;EACL;EACA,OAAO,gBAAgB;EACvB;;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,YAAY;EACZ;EACA,0BAA0B,qBAAqB;EAC/C;EACA,SAAS;EACT;EACA;EACA,SAAS;EACT,QAAQ,UAAU;EAClB,QAAQ;EACR;EACA;EACA;EACA,WAAW;EACX;EACA,WAAW;EACX;EACA,UAAU,mBAAmB,sBAAsB;;cA2e/C,eAAgB,GAAG,SAAS,WAAW;cA2FvC,aAAc,GAAG,SAAS,OAAO;cAoB1B,sBACX,SAAS,4BACR"}
|
package/dist/client/session.js
CHANGED
|
@@ -2,11 +2,11 @@ import { applyOps } from "../wire/ops.js";
|
|
|
2
2
|
import { createMethodProxy } from "../util/methodProxy.js";
|
|
3
3
|
import { StatewireSendError } from "../types.js";
|
|
4
4
|
import { bindCommandsHost } from "../host/host.js";
|
|
5
|
-
import { encodeParams, encodeValue, reviveResponse } from "../wire/meta.js";
|
|
5
|
+
import { encodeParams, encodeValue, reviveResponse, reviveValue } from "../wire/meta.js";
|
|
6
|
+
import { SessionStorageContext, createMemorySessionStore } from "./sessionStore.js";
|
|
6
7
|
import { backoff } from "./timing.js";
|
|
7
|
-
import { createMemorySessionStore } from "./sessionStore.js";
|
|
8
8
|
import { randomId } from "../util/randomId.js";
|
|
9
|
-
import { useEffect, useState } from "@assistant-ui/tap/react-shim";
|
|
9
|
+
import { useContext, useEffect, useState } from "@assistant-ui/tap/react-shim";
|
|
10
10
|
//#region src/client/session.ts
|
|
11
11
|
const protocolError = (message) => {
|
|
12
12
|
throw new Error(`statewire: protocol error: ${message}`);
|
|
@@ -41,7 +41,19 @@ const sleepUntilWake = (s, delayMs) => new Promise((resolve) => {
|
|
|
41
41
|
});
|
|
42
42
|
const setRecord = (s, record) => {
|
|
43
43
|
s.record = record;
|
|
44
|
-
if (!s.
|
|
44
|
+
if (!s.deactivated) s.store.save(record);
|
|
45
|
+
};
|
|
46
|
+
const backlog = (s) => s.pending.filter((p) => p.seq > s.durableSeq).map((p) => ({
|
|
47
|
+
seq: p.seq,
|
|
48
|
+
...p.command
|
|
49
|
+
}));
|
|
50
|
+
const syncBacklog = (s) => {
|
|
51
|
+
const commands = backlog(s);
|
|
52
|
+
if (commands.length === s.record.commands.length && commands.every((c, i) => c.seq === s.record.commands[i].seq)) return;
|
|
53
|
+
setRecord(s, {
|
|
54
|
+
...s.record,
|
|
55
|
+
commands
|
|
56
|
+
});
|
|
45
57
|
};
|
|
46
58
|
const setIdle = (s, level) => {
|
|
47
59
|
if (level === s.idle) return;
|
|
@@ -134,7 +146,8 @@ const resetRecord = (s, clientId) => {
|
|
|
134
146
|
setRecord(s, {
|
|
135
147
|
clientId,
|
|
136
148
|
nextSeq: 1,
|
|
137
|
-
ctxNextSeq: 1
|
|
149
|
+
ctxNextSeq: 1,
|
|
150
|
+
commands: []
|
|
138
151
|
});
|
|
139
152
|
for (const old of survivors) {
|
|
140
153
|
s.lifecycle?.onCovered?.(old.inv);
|
|
@@ -202,6 +215,7 @@ const handleCmd = (s, cmd) => {
|
|
|
202
215
|
};
|
|
203
216
|
const handleCmds = (s, cmds) => {
|
|
204
217
|
for (const cmd of cmds) handleCmd(s, cmd);
|
|
218
|
+
syncBacklog(s);
|
|
205
219
|
wake(s);
|
|
206
220
|
};
|
|
207
221
|
const ingestOps = (s, ops) => {
|
|
@@ -248,6 +262,7 @@ const handleHello = (s, { ops, syn }) => {
|
|
|
248
262
|
s.frameFailures = 0;
|
|
249
263
|
s.resumeAt = 0;
|
|
250
264
|
s.ephemeral = record === "ephemeral";
|
|
265
|
+
if (s.ephemeral) s.deactivated = true;
|
|
251
266
|
const [attachCmd, attachCtx] = s.attachLaneSeqs;
|
|
252
267
|
s.attachLaneSeqs = [/* @__PURE__ */ new Set(), /* @__PURE__ */ new Set()];
|
|
253
268
|
if (record === "resumed") {
|
|
@@ -256,6 +271,7 @@ const handleHello = (s, { ops, syn }) => {
|
|
|
256
271
|
s.ctxPending = s.ctxPending.filter((c) => c.seq > ctxWatermark);
|
|
257
272
|
for (const c of s.ctxPending) c.posted = attachCtx.has(c.seq);
|
|
258
273
|
snapClocks(s, syn.seq ?? 0, ctxWatermark);
|
|
274
|
+
syncBacklog(s);
|
|
259
275
|
} else resetRecord(s, s.record.clientId);
|
|
260
276
|
s.sessionKnown = true;
|
|
261
277
|
publishIngested(s);
|
|
@@ -264,6 +280,7 @@ const handleHello = (s, { ops, syn }) => {
|
|
|
264
280
|
const handleOps = (s, { ops, cmds }) => {
|
|
265
281
|
ingestOps(s, ops);
|
|
266
282
|
for (const cmd of cmds) handleCmd(s, cmd);
|
|
283
|
+
syncBacklog(s);
|
|
267
284
|
publishIngested(s);
|
|
268
285
|
};
|
|
269
286
|
const handleRefusal = (s, syn) => {
|
|
@@ -400,6 +417,7 @@ const start = (s) => {
|
|
|
400
417
|
s.recordPersisted = true;
|
|
401
418
|
s.store.save(s.record);
|
|
402
419
|
}
|
|
420
|
+
resume(s);
|
|
403
421
|
s.running = true;
|
|
404
422
|
pump(s);
|
|
405
423
|
};
|
|
@@ -415,10 +433,6 @@ const setTransport = (s, transport) => {
|
|
|
415
433
|
};
|
|
416
434
|
const enqueue = (s, command, params, resolve, reject) => {
|
|
417
435
|
const seq = s.record.nextSeq;
|
|
418
|
-
setRecord(s, {
|
|
419
|
-
...s.record,
|
|
420
|
-
nextSeq: seq + 1
|
|
421
|
-
});
|
|
422
436
|
const entry = {
|
|
423
437
|
seq,
|
|
424
438
|
command,
|
|
@@ -436,9 +450,38 @@ const enqueue = (s, command, params, resolve, reject) => {
|
|
|
436
450
|
reject
|
|
437
451
|
};
|
|
438
452
|
s.pending.push(entry);
|
|
453
|
+
setRecord(s, {
|
|
454
|
+
...s.record,
|
|
455
|
+
nextSeq: seq + 1,
|
|
456
|
+
commands: backlog(s)
|
|
457
|
+
});
|
|
439
458
|
s.lifecycle?.onStart(entry.inv);
|
|
440
459
|
pump(s);
|
|
441
460
|
};
|
|
461
|
+
const resume = (s) => {
|
|
462
|
+
const stored = [...s.record.commands].sort((a, b) => a.seq - b.seq);
|
|
463
|
+
for (const { seq, ...command } of stored) {
|
|
464
|
+
const params = command.meta === void 0 ? command.params : reviveValue([...command.params], command.meta);
|
|
465
|
+
const entry = {
|
|
466
|
+
seq,
|
|
467
|
+
command,
|
|
468
|
+
inv: {
|
|
469
|
+
method: command.method,
|
|
470
|
+
params,
|
|
471
|
+
ts: Date.now(),
|
|
472
|
+
seq,
|
|
473
|
+
clientId: s.record.clientId
|
|
474
|
+
},
|
|
475
|
+
attempted: true,
|
|
476
|
+
posted: false,
|
|
477
|
+
applied: false,
|
|
478
|
+
resolve: () => {},
|
|
479
|
+
reject: () => {}
|
|
480
|
+
};
|
|
481
|
+
s.pending.push(entry);
|
|
482
|
+
s.lifecycle?.onStart(entry.inv);
|
|
483
|
+
}
|
|
484
|
+
};
|
|
442
485
|
const sendCommand = (s, method, params) => {
|
|
443
486
|
if (!s.running) return rejectedSend({
|
|
444
487
|
fate: "aborted",
|
|
@@ -483,14 +526,18 @@ const setContext = (s, value) => {
|
|
|
483
526
|
};
|
|
484
527
|
const useStatewireSession = (options) => {
|
|
485
528
|
const [defaultStore] = useState(createMemorySessionStore);
|
|
486
|
-
const
|
|
529
|
+
const mounted = useContext(SessionStorageContext);
|
|
530
|
+
if (options.sessionStore !== void 0 && mounted !== void 0) throw new Error("statewire: session storage is set twice — drop the transport's sessionStore or the client's storage.session");
|
|
531
|
+
const store = options.sessionStore ?? mounted ?? defaultStore;
|
|
487
532
|
const [s] = useState(() => {
|
|
488
533
|
const stored = store.load();
|
|
534
|
+
if (stored !== null && !Array.isArray(stored.commands)) throw new Error("statewire: session storage loaded a record without a commands array");
|
|
489
535
|
const session = {
|
|
490
536
|
record: stored ?? {
|
|
491
537
|
clientId: newClientId(),
|
|
492
538
|
nextSeq: 1,
|
|
493
|
-
ctxNextSeq: 1
|
|
539
|
+
ctxNextSeq: 1,
|
|
540
|
+
commands: []
|
|
494
541
|
},
|
|
495
542
|
ctx: {
|
|
496
543
|
get clientId() {
|
|
@@ -508,6 +555,7 @@ const useStatewireSession = (options) => {
|
|
|
508
555
|
recordPersisted: stored !== null,
|
|
509
556
|
store,
|
|
510
557
|
ephemeral: false,
|
|
558
|
+
deactivated: false,
|
|
511
559
|
sessionKnown: false,
|
|
512
560
|
durableSeq: 0,
|
|
513
561
|
resumeAt: 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","names":["useEffect","useState","applyOps","StatewirePath","StatewireCmd","StatewireSyn","encodeParams","encodeValue","reviveResponse","bindCommandsHost","CommandsInvocation","CommandsListener","Statewire","StatewireClient","createMemorySessionStore","StatewireSessionRecord","StatewireSessionStore","StatewireSessionStoreOptions","backoff","createMethodProxy","randomId","StatewireSendError","RemoteLink","StatewireFrame","StatewireRemoteProps","WireCommand","method","params","meta","Pending","seq","command","inv","attempted","posted","applied","resolve","payload","reject","error","CtxPending","ops","protocolError","message","Error","newClientId","rejectedSend","failure","SendFailure","Promise","result","catch","AttachState","StatewireSessionOptions","onDone","Session","record","ctx","recordPersisted","store","ephemeral","sessionKnown","durableSeq","resumeAt","idle","ctxOps","ctxPending","lanesOnAttach","attachLaneSeqs","ReadonlySet","frameFailures","pending","canonical","lastPublished","changed","source","StateSource","attach","attachEpoch","running","pumping","transport","waiters","lifecycle","notify","commands","Record","publish","s","wake","waiter","waitForWake","r","push","sleepUntilWake","delayMs","clearTimeout","timer","setTimeout","filter","w","setRecord","save","setIdle","level","cover","entry","onApplied","onCovered","settleEntry","outcome","ok","p","onSettle","type","settleAll","detach","cause","unmount","gone","superseded","const","batch","detachFailure","predecessorLost","some","fate","restateContext","undefined","ctxNextSeq","resetRecord","clientId","survivors","nextSeq","old","enqueue","ephemeralDetach","rejectedFailure","cmd","code","handleCmd","Number","isSafeInteger","dur","Math","max","find","String","handleCmds","cmds","ingestOps","state","changedPaths","publishIngested","RECORDS","snapClocks","seqWm","ctxWm","tail","cmdAligned","every","i","length","ctxAligned","c","handleHello","syn","includes","attachCmd","attachCtx","Set","has","ctxWatermark","ctxSeq","pump","handleOps","handleRefusal","watermark","retry","Date","now","firstCmd","firstCtx","clocksAhead","connection","reconnect","handleDetach","handleFinish","reason","isFinished","Lanes","frame","cmdEntries","ctxEntries","buildLanes","at","map","takeAttachLanes","wanted","lanes","status","resumeDelay","epoch","send","onPosted","start","stop","setTransport","ts","onStart","sendCommand","encoded","setContext","value","Array","isArray","op","path","useStatewireSession","options","defaultStore","sessionStore","stored","load","session","onHello","args","onOps","onCmds","onRefusal","onIdle","onDetach","onFinish","snapshot","takeChanged","setOnChange","listener","setListener"],"sources":["../../src/client/session.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { applyOps } from \"../wire/ops\";\nimport type { StatewirePath } from \"../wire/ops\";\nimport type { StatewireCmd, StatewireSyn } from \"./connection\";\nimport { encodeParams, encodeValue, reviveResponse } from \"../wire/meta\";\nimport { bindCommandsHost } from \"../host/host\";\nimport type { CommandsInvocation, CommandsListener } from \"../host/host\";\nimport type { Statewire } from \"../types\";\nimport type { StatewireClient } from \"../statewire\";\nimport { createMemorySessionStore } from \"./sessionStore\";\nimport type {\n StatewireSessionRecord,\n StatewireSessionStore,\n StatewireSessionStoreOptions,\n} from \"./sessionStore\";\nimport { backoff } from \"./timing\";\nimport { createMethodProxy } from \"../util/methodProxy\";\nimport { randomId } from \"../util/randomId\";\nimport { StatewireSendError } from \"../types\";\nimport type {\n RemoteLink,\n StatewireFrame,\n StatewireRemoteProps,\n} from \"../transports/sse\";\n\ntype WireCommand = {\n method: string;\n params: unknown[];\n meta?: unknown;\n};\n\ntype Pending = {\n readonly seq: number;\n readonly command: WireCommand;\n readonly inv: CommandsInvocation;\n /** A send carrying this statement may have reached the server. */\n attempted: boolean;\n /** In the resend queue when false. */\n posted: boolean;\n /** The server acknowledged admission; the terminal verdict is still due. */\n applied: boolean;\n resolve: (payload: unknown) => void;\n reject: (error: StatewireSendError) => void;\n};\n\ntype CtxPending = {\n readonly seq: number;\n readonly ops: unknown[];\n posted: boolean;\n};\n\nconst protocolError = (message: string): never => {\n throw new Error(`statewire: protocol error: ${message}`);\n};\n\nconst newClientId = () => `c-${randomId()}`;\n\nconst rejectedSend = (\n failure: StatewireClient.SendFailure,\n): Promise<unknown> => {\n const result = Promise.reject<unknown>(new StatewireSendError(failure));\n result.catch(() => {});\n return result;\n};\n\ntype AttachState = \"detached\" | \"attached\" | \"finished\";\n\nexport type StatewireSessionOptions = StatewireSessionStoreOptions & {\n onDone?: (() => void) | undefined;\n};\n\ntype Session = {\n record: StatewireSessionRecord;\n ctx: StatewireRemoteProps;\n recordPersisted: boolean;\n store: StatewireSessionStore;\n ephemeral: boolean;\n sessionKnown: boolean;\n durableSeq: number;\n resumeAt: number;\n idle: boolean;\n onDone: (() => void) | undefined;\n ctxOps: unknown[] | undefined;\n ctxPending: CtxPending[];\n lanesOnAttach: boolean;\n attachLaneSeqs: readonly [ReadonlySet<number>, ReadonlySet<number>];\n frameFailures: number;\n pending: Pending[];\n canonical: unknown;\n lastPublished: unknown;\n changed: StatewirePath[];\n source: Statewire.StateSource;\n attach: AttachState;\n attachEpoch: number;\n running: boolean;\n pumping: boolean;\n transport: RemoteLink | null;\n waiters: (() => void)[];\n lifecycle: CommandsListener | null;\n notify: () => void;\n commands: Record<string, (...params: unknown[]) => Promise<unknown>>;\n};\n\nconst publish = (s: Session) => {\n if (s.canonical === s.lastPublished) return;\n s.lastPublished = s.canonical;\n s.notify();\n};\n\nconst wake = (s: Session) => {\n const waiters = s.waiters;\n s.waiters = [];\n for (const waiter of waiters) waiter();\n};\n\nconst waitForWake = (s: Session) => new Promise<void>((r) => s.waiters.push(r));\n\nconst sleepUntilWake = (s: Session, delayMs: number) =>\n new Promise<void>((resolve) => {\n const waiter = () => {\n clearTimeout(timer);\n resolve();\n };\n const timer = setTimeout(() => {\n s.waiters = s.waiters.filter((w) => w !== waiter);\n resolve();\n }, delayMs);\n s.waiters.push(waiter);\n });\n\nconst setRecord = (s: Session, record: StatewireSessionRecord) => {\n s.record = record;\n if (!s.ephemeral) s.store.save(record);\n};\n\nconst setIdle = (s: Session, level: boolean) => {\n if (level === s.idle) return;\n s.idle = level;\n s.notify();\n if (level) s.onDone?.();\n};\n\nconst cover = (s: Session, entry: Pending) => {\n if (entry.applied) return;\n entry.applied = true;\n s.lifecycle?.onApplied(entry.inv);\n s.lifecycle?.onCovered?.(entry.inv);\n};\n\nconst settleEntry = (\n s: Session,\n entry: Pending,\n outcome:\n | { ok: true; payload: unknown }\n | { ok: false; failure: StatewireClient.SendFailure },\n) => {\n s.pending = s.pending.filter((p) => p !== entry);\n if (outcome.ok) {\n entry.resolve(outcome.payload);\n s.lifecycle?.onSettle(entry.inv, {\n type: \"accepted\",\n payload: outcome.payload,\n });\n } else {\n entry.reject(new StatewireSendError(outcome.failure));\n s.lifecycle?.onSettle(entry.inv, {\n type: \"rejected\",\n error: new StatewireSendError(outcome.failure),\n });\n }\n};\n\nconst settleAll = (s: Session, detach: \"unmount\" | \"gone\" | \"superseded\") => {\n const cause = (\n {\n unmount: \"unmount\",\n gone: \"stream-gone\",\n superseded: \"superseded\",\n } as const\n )[detach];\n const batch = s.pending;\n const detachFailure = (entry: Pending): StatewireClient.SendFailure => {\n if (!entry.attempted) {\n const predecessorLost = batch.some(\n (p) => p.seq < entry.seq && p.attempted,\n );\n return predecessorLost\n ? { fate: \"aborted\", cause, predecessorLost }\n : { fate: \"aborted\", cause };\n }\n return { fate: \"lost\", cause };\n };\n for (const entry of [...batch]) {\n s.lifecycle?.onCovered?.(entry.inv);\n settleEntry(s, entry, { ok: false, failure: detachFailure(entry) });\n }\n};\n\nconst restateContext = (s: Session) => {\n s.ctxPending = [];\n if (s.ctxOps === undefined) return;\n const seq = s.record.ctxNextSeq;\n setRecord(s, { ...s.record, ctxNextSeq: seq + 1 });\n s.ctxPending.push({ seq, ops: s.ctxOps, posted: false });\n};\n\n// The server-side record's dedup memory is gone: history that may have\n// reached a server settles lost — the new hello's snapshot is the only\n// truth about what survived — and never-attempted statements re-stamp\n// from 1 on the reset lane clocks.\nconst resetRecord = (s: Session, clientId: string) => {\n const survivors: Pending[] = [];\n for (const entry of [...s.pending]) {\n if (entry.attempted || entry.applied) {\n s.lifecycle?.onCovered?.(entry.inv);\n settleEntry(s, entry, {\n ok: false,\n failure: { fate: \"lost\", cause: \"session-lost\" },\n });\n } else {\n survivors.push(entry);\n }\n }\n s.pending = [];\n s.durableSeq = 0;\n setRecord(s, { clientId, nextSeq: 1, ctxNextSeq: 1 });\n for (const old of survivors) {\n s.lifecycle?.onCovered?.(old.inv);\n enqueue(s, old.command, old.inv.params, old.resolve, old.reject);\n }\n restateContext(s);\n publish(s);\n wake(s);\n};\n\n// The record died with the attach: mint a fresh id for the next one.\nconst ephemeralDetach = (s: Session) => {\n s.ephemeral = false;\n s.sessionKnown = false;\n resetRecord(s, newClientId());\n};\n\nconst rejectedFailure = (cmd: StatewireCmd): StatewireClient.SendFailure => {\n return {\n fate: \"rejected\",\n cause: \"server\",\n ...(cmd.code !== undefined && { code: cmd.code }),\n ...(cmd.message !== undefined && { message: cmd.message }),\n ...(cmd.payload !== undefined && { payload: cmd.payload }),\n };\n};\n\nconst handleCmd = (s: Session, cmd: StatewireCmd) => {\n if (!Number.isSafeInteger(cmd.seq) || cmd.seq <= 0) {\n protocolError(\"cmd.seq must be a positive safe integer\");\n }\n if (cmd.dur === true) {\n s.durableSeq = Math.max(s.durableSeq, cmd.seq);\n for (const entry of s.pending) {\n if (entry.seq <= s.durableSeq) entry.posted = true;\n }\n }\n if (cmd.type === undefined) return;\n const entry = s.pending.find((p) => p.seq === cmd.seq);\n if (cmd.type === \"applied\") {\n if (entry) cover(s, entry);\n return;\n }\n if (!entry) return;\n cover(s, entry);\n const outcome =\n cmd.type === \"result\"\n ? { ok: true as const, payload: reviveResponse(cmd).payload }\n : cmd.type === \"rejected\"\n ? { ok: false as const, failure: rejectedFailure(reviveResponse(cmd)) }\n : cmd.type === \"failed\"\n ? {\n ok: false as const,\n failure: {\n fate: \"failed\" as const,\n cause: \"server\" as const,\n ...(cmd.message !== undefined && { message: cmd.message }),\n },\n }\n : cmd.type === \"crashed\"\n ? {\n ok: false as const,\n failure: { fate: \"crashed\" as const, cause: \"server\" as const },\n }\n : cmd.type === \"expired\"\n ? {\n ok: false as const,\n failure: {\n fate: \"expired\" as const,\n cause: \"server\" as const,\n },\n }\n : protocolError(`unknown cmd type: ${String(cmd.type)}`);\n settleEntry(s, entry, outcome);\n};\n\nconst handleCmds = (s: Session, cmds: StatewireCmd[]) => {\n for (const cmd of cmds) handleCmd(s, cmd);\n wake(s);\n};\n\nconst ingestOps = (s: Session, ops: unknown[]) => {\n const applied = applyOps(s.canonical, ops);\n s.canonical = applied.state;\n s.changed.push(...applied.changedPaths);\n};\n\nconst publishIngested = (s: Session) => {\n publish(s);\n wake(s);\n};\n\nconst RECORDS = [\"fresh\", \"resumed\", \"ephemeral\"];\n\n// Snap a resumed session's lane clocks onto the record: seqs above the stated\n// watermark were never admitted, so a stale restored clock re-stamps its\n// never-admitted statements consecutively after the watermark.\nconst snapClocks = (s: Session, seqWm: number, ctxWm: number) => {\n const tail = s.pending.filter((p) => p.seq > seqWm);\n const cmdAligned =\n tail.every((p, i) => p.seq === seqWm + 1 + i) &&\n s.record.nextSeq === seqWm + tail.length + 1;\n if (!cmdAligned) {\n s.pending = s.pending.filter((p) => p.seq <= seqWm);\n setRecord(s, { ...s.record, nextSeq: seqWm + 1 });\n for (const old of tail) {\n s.lifecycle?.onCovered?.(old.inv);\n enqueue(s, old.command, old.inv.params, old.resolve, old.reject);\n }\n }\n const ctxAligned =\n s.ctxPending.every((c, i) => c.seq === ctxWm + 1 + i) &&\n s.record.ctxNextSeq === ctxWm + s.ctxPending.length + 1;\n if (!ctxAligned) {\n setRecord(s, { ...s.record, ctxNextSeq: ctxWm + 1 });\n restateContext(s);\n }\n};\n\nconst handleHello = (\n s: Session,\n { ops, syn }: { ops: unknown[]; syn: StatewireSyn },\n) => {\n const record = syn.record ?? \"fresh\";\n if (!RECORDS.includes(record)) {\n protocolError(`unknown record: ${String(record)}`);\n }\n ingestOps(s, ops);\n s.attach = \"attached\";\n s.attachEpoch += 1;\n s.frameFailures = 0;\n s.resumeAt = 0;\n s.ephemeral = record === \"ephemeral\";\n const [attachCmd, attachCtx] = s.attachLaneSeqs;\n s.attachLaneSeqs = [new Set(), new Set()];\n if (record === \"resumed\") {\n // Resending unsettled statements is exactly-once-safe: absorbed replays\n // re-emit their stored verdicts (applied while still in flight).\n for (const entry of s.pending) {\n entry.posted = attachCmd.has(entry.seq);\n }\n const ctxWatermark = syn.ctxSeq ?? 0;\n s.ctxPending = s.ctxPending.filter((c) => c.seq > ctxWatermark);\n for (const c of s.ctxPending) c.posted = attachCtx.has(c.seq);\n snapClocks(s, syn.seq ?? 0, ctxWatermark);\n } else {\n resetRecord(s, s.record.clientId);\n }\n s.sessionKnown = true;\n publishIngested(s);\n void pump(s);\n};\n\nconst handleOps = (\n s: Session,\n { ops, cmds }: { ops: unknown[]; cmds: StatewireCmd[] },\n) => {\n ingestOps(s, ops);\n for (const cmd of cmds) handleCmd(s, cmd);\n publishIngested(s);\n};\n\n// A {syn} refusal never settles: reconcile the resend queues against the\n// stated watermarks; retry present means wait, absent means resend now.\nconst handleRefusal = (s: Session, syn: StatewireSyn) => {\n const { seq, ctxSeq } = syn;\n if (typeof seq === \"number\") {\n const watermark = Math.max(seq, s.durableSeq);\n for (const entry of s.pending) entry.posted = entry.seq <= watermark;\n }\n if (typeof ctxSeq === \"number\") {\n s.ctxPending = s.ctxPending.filter((c) => c.seq > ctxSeq);\n for (const c of s.ctxPending) c.posted = false;\n }\n if (typeof syn.retry === \"number\") {\n s.resumeAt = Date.now() + syn.retry;\n }\n const firstCmd = s.pending.find((p) => !p.posted);\n const firstCtx = s.ctxPending.find((c) => !c.posted);\n const clocksAhead =\n (typeof syn.seq === \"number\" &&\n firstCmd !== undefined &&\n firstCmd.seq > Math.max(syn.seq, s.durableSeq) + 1) ||\n (typeof syn.ctxSeq === \"number\" &&\n firstCtx !== undefined &&\n firstCtx.seq > syn.ctxSeq + 1);\n // Clocks ahead of a record that cannot admit them: reattach first and let\n // the hello's record arbitrate.\n if (clocksAhead) s.transport?.connection.reconnect();\n wake(s);\n void pump(s);\n};\n\nconst handleDetach = (s: Session) => {\n if (s.attach === \"attached\") s.attach = \"detached\";\n if (s.ephemeral) ephemeralDetach(s);\n wake(s);\n};\n\nconst handleFinish = (s: Session, { reason }: { reason: string }) => {\n if (reason === \"evicted\" || reason === \"error\") return;\n s.attach = \"finished\";\n settleAll(s, reason === \"superseded\" ? \"superseded\" : \"gone\");\n publish(s);\n wake(s);\n};\n\nconst isFinished = (s: Session) => s.attach === \"finished\";\n\ntype Lanes = {\n frame: StatewireFrame;\n cmdEntries: Pending[];\n ctxEntries: CtxPending[];\n};\n\n// A lane must be strictly consecutive within a frame: the first unposted run\n// per lane rides now, later runs ride follow-up frames.\nconst buildLanes = (s: Session): Lanes | null => {\n const ctxEntries: CtxPending[] = [];\n for (const entry of s.ctxPending) {\n if (entry.posted) continue;\n if (ctxEntries.length > 0 && entry.seq !== ctxEntries.at(-1)!.seq + 1)\n break;\n ctxEntries.push(entry);\n }\n const cmdEntries: Pending[] = [];\n for (const entry of s.pending) {\n if (entry.posted) continue;\n if (cmdEntries.length > 0 && entry.seq !== cmdEntries.at(-1)!.seq + 1)\n break;\n cmdEntries.push(entry);\n }\n if (ctxEntries.length === 0 && cmdEntries.length === 0) return null;\n return {\n frame: {\n ...(ctxEntries.length > 0 && {\n ctx: ctxEntries.map((c) => ({ ops: c.ops, seq: c.seq })),\n }),\n ...(cmdEntries.length > 0 && {\n cmd: cmdEntries.map((p) => ({ ...p.command, seq: p.seq })),\n }),\n },\n cmdEntries,\n ctxEntries,\n };\n};\n\nconst takeAttachLanes = (s: Session): StatewireFrame | null => {\n s.attachLaneSeqs = [new Set(), new Set()];\n const wanted = s.lanesOnAttach;\n s.lanesOnAttach = false;\n if (!wanted || !s.sessionKnown) return null;\n const lanes = buildLanes(s);\n if (!lanes) return null;\n s.attachLaneSeqs = [\n new Set(lanes.cmdEntries.map((p) => p.seq)),\n new Set(lanes.ctxEntries.map((c) => c.seq)),\n ];\n for (const entry of lanes.cmdEntries) entry.attempted = true;\n return lanes.frame;\n};\n\nconst pump = async (s: Session) => {\n if (s.pumping) return;\n s.pumping = true;\n try {\n while (s.running && !isFinished(s)) {\n const transport = s.transport;\n if (buildLanes(s) === null) return;\n if (s.attach !== \"attached\" || !transport) {\n if (transport?.connection.status === \"idle\") {\n transport.connection.reconnect();\n }\n await waitForWake(s);\n continue;\n }\n const resumeDelay = s.resumeAt - Date.now();\n if (resumeDelay > 0) {\n await sleepUntilWake(s, resumeDelay);\n continue;\n }\n const lanes = buildLanes(s);\n if (!lanes) continue;\n const epoch = s.attachEpoch;\n for (const entry of lanes.cmdEntries) entry.attempted = true;\n let result;\n try {\n result = await transport.send(lanes.frame);\n } catch {\n if (!s.running || isFinished(s)) return;\n s.frameFailures += 1;\n await sleepUntilWake(s, backoff(s.frameFailures));\n continue;\n }\n if (!s.running) return;\n s.frameFailures = 0;\n if (result.type === \"ok\") {\n if (epoch === s.attachEpoch) {\n for (const entry of lanes.cmdEntries) {\n if (s.pending.includes(entry)) {\n entry.posted = true;\n s.lifecycle?.onPosted?.(entry.inv);\n }\n }\n for (const entry of lanes.ctxEntries) entry.posted = true;\n }\n } else if (result.type === \"refusal\") {\n if (epoch === s.attachEpoch) handleRefusal(s, result.syn);\n } else if (result.type === \"stale-lease\") {\n // A POST outlived its attach: reattach first, the hello arbitrates.\n if (epoch === s.attachEpoch) {\n transport.connection.reconnect();\n await waitForWake(s);\n }\n } else if (result.type === \"no-lease\") {\n // No out-of-band write channel: the frame rides a fresh attach.\n if (epoch === s.attachEpoch) {\n s.lanesOnAttach = true;\n transport.connection.reconnect();\n while (s.running && !isFinished(s) && epoch === s.attachEpoch) {\n await waitForWake(s);\n }\n }\n }\n }\n } finally {\n s.pumping = false;\n }\n};\n\nconst start = (s: Session) => {\n if (!s.recordPersisted) {\n s.recordPersisted = true;\n s.store.save(s.record);\n }\n s.running = true;\n void pump(s);\n};\n\nconst stop = (s: Session) => {\n s.running = false;\n wake(s);\n settleAll(s, \"unmount\");\n};\n\nconst setTransport = (s: Session, transport: RemoteLink) => {\n if (s.transport === transport) return;\n s.transport = transport;\n wake(s);\n};\n\nconst enqueue = (\n s: Session,\n command: WireCommand,\n params: readonly unknown[],\n resolve: (payload: unknown) => void,\n reject: (error: StatewireSendError) => void,\n) => {\n const seq = s.record.nextSeq;\n setRecord(s, { ...s.record, nextSeq: seq + 1 });\n const entry: Pending = {\n seq,\n command,\n inv: {\n method: command.method,\n params,\n ts: Date.now(),\n seq,\n clientId: s.record.clientId,\n },\n attempted: false,\n posted: false,\n applied: false,\n resolve,\n reject,\n };\n s.pending.push(entry);\n s.lifecycle?.onStart(entry.inv);\n void pump(s);\n};\n\nconst sendCommand = (\n s: Session,\n method: string,\n params: readonly unknown[],\n): Promise<unknown> => {\n if (!s.running) {\n return rejectedSend({ fate: \"aborted\", cause: \"unmount\" });\n }\n if (isFinished(s)) {\n return rejectedSend({ fate: \"aborted\", cause: \"stream-gone\" });\n }\n const result = new Promise<unknown>((resolve, reject) => {\n const { params: encoded, meta } = encodeParams(params);\n enqueue(\n s,\n { method, params: encoded, ...(meta !== undefined && { meta }) },\n params,\n resolve,\n reject,\n );\n });\n result.catch(() => {});\n return result;\n};\n\nconst setContext = (s: Session, value: Record<string, unknown>): void => {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new Error(\"statewire: context must be a plain object\");\n }\n const { value: encoded, meta } = encodeValue(value);\n s.ctxOps = [\n {\n op: \"replace\",\n path: [],\n value: encoded,\n ...(meta !== undefined && { meta }),\n },\n ];\n const seq = s.record.ctxNextSeq;\n setRecord(s, { ...s.record, ctxNextSeq: seq + 1 });\n s.ctxPending.push({ seq, ops: s.ctxOps, posted: false });\n wake(s);\n void pump(s);\n};\n\nexport const useStatewireSession = (\n options: StatewireSessionOptions,\n): Session => {\n const [defaultStore] = useState(createMemorySessionStore);\n const store = options.sessionStore ?? defaultStore;\n const [s] = useState((): Session => {\n const stored = store.load();\n const record = stored ?? {\n clientId: newClientId(),\n nextSeq: 1,\n ctxNextSeq: 1,\n };\n const session: Session = {\n record,\n ctx: {\n get clientId() {\n return session.record.clientId;\n },\n takeAttachLanes: () => takeAttachLanes(session),\n onHello: (args) => handleHello(session, args),\n onOps: (args) => handleOps(session, args),\n onCmds: (cmds) => handleCmds(session, cmds),\n onRefusal: (syn) => handleRefusal(session, syn),\n onIdle: (level) => setIdle(session, level),\n onDetach: () => handleDetach(session),\n onFinish: (args) => handleFinish(session, args),\n },\n recordPersisted: stored !== null,\n store,\n ephemeral: false,\n sessionKnown: false,\n durableSeq: 0,\n resumeAt: 0,\n idle: false,\n onDone: undefined,\n ctxOps: undefined,\n ctxPending: [],\n lanesOnAttach: false,\n attachLaneSeqs: [new Set(), new Set()],\n frameFailures: 0,\n pending: [],\n canonical: undefined,\n lastPublished: undefined,\n changed: [],\n source: {\n snapshot: () => session.canonical,\n takeChanged: () => {\n const changed = session.changed;\n session.changed = [];\n return changed;\n },\n setOnChange: (listener) => {\n session.notify = listener ?? (() => {});\n },\n },\n attach: \"detached\",\n attachEpoch: 0,\n running: false,\n pumping: false,\n transport: null,\n waiters: [],\n lifecycle: null,\n notify: () => {},\n commands: createMethodProxy((method, params) =>\n sendCommand(session, method, params),\n ),\n };\n bindCommandsHost(session.commands, {\n setListener: (listener) => {\n session.lifecycle = listener;\n },\n });\n return session;\n });\n s.store = store;\n s.onDone = options.onDone;\n useEffect(() => {\n start(s);\n return () => stop(s);\n }, [s]);\n return s;\n};\n\nexport { setContext, setTransport };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"session.js","names":["useContext","useEffect","useState","applyOps","StatewirePath","StatewireCmd","StatewireSyn","encodeParams","encodeValue","reviveResponse","reviveValue","bindCommandsHost","CommandsInvocation","CommandsListener","Statewire","StatewireClient","SessionStorageContext","createMemorySessionStore","backoff","createMethodProxy","randomId","StatewireSendError","RemoteLink","StatewireFrame","StatewireRemoteProps","WireCommand","method","params","meta","Pending","seq","command","inv","attempted","posted","applied","resolve","payload","reject","error","CtxPending","ops","protocolError","message","Error","newClientId","rejectedSend","failure","SendFailure","Promise","result","catch","AttachState","StatewireSessionOptions","sessionStore","SessionStorage","onDone","Session","record","SessionRecord","ctx","recordPersisted","store","ephemeral","deactivated","sessionKnown","durableSeq","resumeAt","idle","ctxOps","ctxPending","lanesOnAttach","attachLaneSeqs","ReadonlySet","frameFailures","pending","canonical","lastPublished","changed","source","StateSource","attach","attachEpoch","running","pumping","transport","waiters","lifecycle","notify","commands","Record","publish","s","wake","waiter","waitForWake","r","push","sleepUntilWake","delayMs","clearTimeout","timer","setTimeout","filter","w","setRecord","save","backlog","SessionCommand","p","map","syncBacklog","unchanged","length","every","c","i","setIdle","level","cover","entry","onApplied","onCovered","settleEntry","outcome","ok","onSettle","type","settleAll","detach","cause","unmount","gone","superseded","const","batch","detachFailure","predecessorLost","some","fate","restateContext","undefined","ctxNextSeq","resetRecord","clientId","survivors","nextSeq","old","enqueue","ephemeralDetach","rejectedFailure","cmd","code","handleCmd","Number","isSafeInteger","dur","Math","max","find","String","handleCmds","cmds","ingestOps","state","changedPaths","publishIngested","RECORDS","snapClocks","seqWm","ctxWm","tail","cmdAligned","ctxAligned","handleHello","syn","includes","attachCmd","attachCtx","Set","has","ctxWatermark","ctxSeq","pump","handleOps","handleRefusal","watermark","retry","Date","now","firstCmd","firstCtx","clocksAhead","connection","reconnect","handleDetach","handleFinish","reason","isFinished","Lanes","frame","cmdEntries","ctxEntries","buildLanes","at","takeAttachLanes","wanted","lanes","status","resumeDelay","epoch","send","onPosted","start","resume","stop","setTransport","ts","onStart","stored","sort","a","b","sendCommand","encoded","setContext","value","Array","isArray","op","path","useStatewireSession","options","defaultStore","mounted","load","session","onHello","args","onOps","onCmds","onRefusal","onIdle","onDetach","onFinish","snapshot","takeChanged","setOnChange","listener","setListener"],"sources":["../../src/client/session.ts"],"sourcesContent":["import { useContext, useEffect, useState } from \"react\";\nimport { applyOps } from \"../wire/ops\";\nimport type { StatewirePath } from \"../wire/ops\";\nimport type { StatewireCmd, StatewireSyn } from \"./connection\";\nimport {\n encodeParams,\n encodeValue,\n reviveResponse,\n reviveValue,\n} from \"../wire/meta\";\nimport { bindCommandsHost } from \"../host/host\";\nimport type { CommandsInvocation, CommandsListener } from \"../host/host\";\nimport type { Statewire } from \"../types\";\nimport type { StatewireClient } from \"../statewire\";\nimport {\n SessionStorageContext,\n createMemorySessionStore,\n} from \"./sessionStore\";\nimport { backoff } from \"./timing\";\nimport { createMethodProxy } from \"../util/methodProxy\";\nimport { randomId } from \"../util/randomId\";\nimport { StatewireSendError } from \"../types\";\nimport type {\n RemoteLink,\n StatewireFrame,\n StatewireRemoteProps,\n} from \"../transports/sse\";\n\ntype WireCommand = {\n method: string;\n params: unknown[];\n meta?: unknown;\n};\n\ntype Pending = {\n readonly seq: number;\n readonly command: WireCommand;\n readonly inv: CommandsInvocation;\n /** A send carrying this statement may have reached the server. */\n attempted: boolean;\n /** In the resend queue when false. */\n posted: boolean;\n /** The server acknowledged admission; the terminal verdict is still due. */\n applied: boolean;\n resolve: (payload: unknown) => void;\n reject: (error: StatewireSendError) => void;\n};\n\ntype CtxPending = {\n readonly seq: number;\n readonly ops: unknown[];\n posted: boolean;\n};\n\nconst protocolError = (message: string): never => {\n throw new Error(`statewire: protocol error: ${message}`);\n};\n\nconst newClientId = () => `c-${randomId()}`;\n\nconst rejectedSend = (\n failure: StatewireClient.SendFailure,\n): Promise<unknown> => {\n const result = Promise.reject<unknown>(new StatewireSendError(failure));\n result.catch(() => {});\n return result;\n};\n\ntype AttachState = \"detached\" | \"attached\" | \"finished\";\n\nexport type StatewireSessionOptions = {\n sessionStore?: StatewireClient.SessionStorage | undefined;\n onDone?: (() => void) | undefined;\n};\n\ntype Session = {\n record: StatewireClient.SessionRecord;\n ctx: StatewireRemoteProps;\n recordPersisted: boolean;\n store: StatewireClient.SessionStorage;\n ephemeral: boolean;\n /** An ephemeral record died with its attach: the storage is off for good. */\n deactivated: boolean;\n sessionKnown: boolean;\n durableSeq: number;\n resumeAt: number;\n idle: boolean;\n onDone: (() => void) | undefined;\n ctxOps: unknown[] | undefined;\n ctxPending: CtxPending[];\n lanesOnAttach: boolean;\n attachLaneSeqs: readonly [ReadonlySet<number>, ReadonlySet<number>];\n frameFailures: number;\n pending: Pending[];\n canonical: unknown;\n lastPublished: unknown;\n changed: StatewirePath[];\n source: Statewire.StateSource;\n attach: AttachState;\n attachEpoch: number;\n running: boolean;\n pumping: boolean;\n transport: RemoteLink | null;\n waiters: (() => void)[];\n lifecycle: CommandsListener | null;\n notify: () => void;\n commands: Record<string, (...params: unknown[]) => Promise<unknown>>;\n};\n\nconst publish = (s: Session) => {\n if (s.canonical === s.lastPublished) return;\n s.lastPublished = s.canonical;\n s.notify();\n};\n\nconst wake = (s: Session) => {\n const waiters = s.waiters;\n s.waiters = [];\n for (const waiter of waiters) waiter();\n};\n\nconst waitForWake = (s: Session) => new Promise<void>((r) => s.waiters.push(r));\n\nconst sleepUntilWake = (s: Session, delayMs: number) =>\n new Promise<void>((resolve) => {\n const waiter = () => {\n clearTimeout(timer);\n resolve();\n };\n const timer = setTimeout(() => {\n s.waiters = s.waiters.filter((w) => w !== waiter);\n resolve();\n }, delayMs);\n s.waiters.push(waiter);\n });\n\nconst setRecord = (s: Session, record: StatewireClient.SessionRecord) => {\n s.record = record;\n if (!s.deactivated) s.store.save(record);\n};\n\nconst backlog = (s: Session): StatewireClient.SessionCommand[] =>\n s.pending\n .filter((p) => p.seq > s.durableSeq)\n .map((p) => ({ seq: p.seq, ...p.command }));\n\n// Storage holds what a reload must resend: a command leaves at its dur, at a\n// server verdict, or at a record reset — never at a local channel death.\nconst syncBacklog = (s: Session) => {\n const commands = backlog(s);\n const unchanged =\n commands.length === s.record.commands.length &&\n commands.every((c, i) => c.seq === s.record.commands[i]!.seq);\n if (unchanged) return;\n setRecord(s, { ...s.record, commands });\n};\n\nconst setIdle = (s: Session, level: boolean) => {\n if (level === s.idle) return;\n s.idle = level;\n s.notify();\n if (level) s.onDone?.();\n};\n\nconst cover = (s: Session, entry: Pending) => {\n if (entry.applied) return;\n entry.applied = true;\n s.lifecycle?.onApplied(entry.inv);\n s.lifecycle?.onCovered?.(entry.inv);\n};\n\nconst settleEntry = (\n s: Session,\n entry: Pending,\n outcome:\n | { ok: true; payload: unknown }\n | { ok: false; failure: StatewireClient.SendFailure },\n) => {\n s.pending = s.pending.filter((p) => p !== entry);\n if (outcome.ok) {\n entry.resolve(outcome.payload);\n s.lifecycle?.onSettle(entry.inv, {\n type: \"accepted\",\n payload: outcome.payload,\n });\n } else {\n entry.reject(new StatewireSendError(outcome.failure));\n s.lifecycle?.onSettle(entry.inv, {\n type: \"rejected\",\n error: new StatewireSendError(outcome.failure),\n });\n }\n};\n\nconst settleAll = (s: Session, detach: \"unmount\" | \"gone\" | \"superseded\") => {\n const cause = (\n {\n unmount: \"unmount\",\n gone: \"stream-gone\",\n superseded: \"superseded\",\n } as const\n )[detach];\n const batch = s.pending;\n const detachFailure = (entry: Pending): StatewireClient.SendFailure => {\n if (!entry.attempted) {\n const predecessorLost = batch.some(\n (p) => p.seq < entry.seq && p.attempted,\n );\n return predecessorLost\n ? { fate: \"aborted\", cause, predecessorLost }\n : { fate: \"aborted\", cause };\n }\n return { fate: \"lost\", cause };\n };\n for (const entry of [...batch]) {\n s.lifecycle?.onCovered?.(entry.inv);\n settleEntry(s, entry, { ok: false, failure: detachFailure(entry) });\n }\n};\n\nconst restateContext = (s: Session) => {\n s.ctxPending = [];\n if (s.ctxOps === undefined) return;\n const seq = s.record.ctxNextSeq;\n setRecord(s, { ...s.record, ctxNextSeq: seq + 1 });\n s.ctxPending.push({ seq, ops: s.ctxOps, posted: false });\n};\n\n// The server-side record's dedup memory is gone: history that may have\n// reached a server settles lost — the new hello's snapshot is the only\n// truth about what survived — and never-attempted statements re-stamp\n// from 1 on the reset lane clocks.\nconst resetRecord = (s: Session, clientId: string) => {\n const survivors: Pending[] = [];\n for (const entry of [...s.pending]) {\n if (entry.attempted || entry.applied) {\n s.lifecycle?.onCovered?.(entry.inv);\n settleEntry(s, entry, {\n ok: false,\n failure: { fate: \"lost\", cause: \"session-lost\" },\n });\n } else {\n survivors.push(entry);\n }\n }\n s.pending = [];\n s.durableSeq = 0;\n setRecord(s, { clientId, nextSeq: 1, ctxNextSeq: 1, commands: [] });\n for (const old of survivors) {\n s.lifecycle?.onCovered?.(old.inv);\n enqueue(s, old.command, old.inv.params, old.resolve, old.reject);\n }\n restateContext(s);\n publish(s);\n wake(s);\n};\n\n// The record died with the attach: mint a fresh id for the next one.\nconst ephemeralDetach = (s: Session) => {\n s.ephemeral = false;\n s.sessionKnown = false;\n resetRecord(s, newClientId());\n};\n\nconst rejectedFailure = (cmd: StatewireCmd): StatewireClient.SendFailure => {\n return {\n fate: \"rejected\",\n cause: \"server\",\n ...(cmd.code !== undefined && { code: cmd.code }),\n ...(cmd.message !== undefined && { message: cmd.message }),\n ...(cmd.payload !== undefined && { payload: cmd.payload }),\n };\n};\n\nconst handleCmd = (s: Session, cmd: StatewireCmd) => {\n if (!Number.isSafeInteger(cmd.seq) || cmd.seq <= 0) {\n protocolError(\"cmd.seq must be a positive safe integer\");\n }\n if (cmd.dur === true) {\n s.durableSeq = Math.max(s.durableSeq, cmd.seq);\n for (const entry of s.pending) {\n if (entry.seq <= s.durableSeq) entry.posted = true;\n }\n }\n if (cmd.type === undefined) return;\n const entry = s.pending.find((p) => p.seq === cmd.seq);\n if (cmd.type === \"applied\") {\n if (entry) cover(s, entry);\n return;\n }\n if (!entry) return;\n cover(s, entry);\n const outcome =\n cmd.type === \"result\"\n ? { ok: true as const, payload: reviveResponse(cmd).payload }\n : cmd.type === \"rejected\"\n ? { ok: false as const, failure: rejectedFailure(reviveResponse(cmd)) }\n : cmd.type === \"failed\"\n ? {\n ok: false as const,\n failure: {\n fate: \"failed\" as const,\n cause: \"server\" as const,\n ...(cmd.message !== undefined && { message: cmd.message }),\n },\n }\n : cmd.type === \"crashed\"\n ? {\n ok: false as const,\n failure: { fate: \"crashed\" as const, cause: \"server\" as const },\n }\n : cmd.type === \"expired\"\n ? {\n ok: false as const,\n failure: {\n fate: \"expired\" as const,\n cause: \"server\" as const,\n },\n }\n : protocolError(`unknown cmd type: ${String(cmd.type)}`);\n settleEntry(s, entry, outcome);\n};\n\nconst handleCmds = (s: Session, cmds: StatewireCmd[]) => {\n for (const cmd of cmds) handleCmd(s, cmd);\n syncBacklog(s);\n wake(s);\n};\n\nconst ingestOps = (s: Session, ops: unknown[]) => {\n const applied = applyOps(s.canonical, ops);\n s.canonical = applied.state;\n s.changed.push(...applied.changedPaths);\n};\n\nconst publishIngested = (s: Session) => {\n publish(s);\n wake(s);\n};\n\nconst RECORDS = [\"fresh\", \"resumed\", \"ephemeral\"];\n\n// Snap a resumed session's lane clocks onto the record: seqs above the stated\n// watermark were never admitted, so a stale restored clock re-stamps its\n// never-admitted statements consecutively after the watermark.\nconst snapClocks = (s: Session, seqWm: number, ctxWm: number) => {\n const tail = s.pending.filter((p) => p.seq > seqWm);\n const cmdAligned =\n tail.every((p, i) => p.seq === seqWm + 1 + i) &&\n s.record.nextSeq === seqWm + tail.length + 1;\n if (!cmdAligned) {\n s.pending = s.pending.filter((p) => p.seq <= seqWm);\n setRecord(s, { ...s.record, nextSeq: seqWm + 1 });\n for (const old of tail) {\n s.lifecycle?.onCovered?.(old.inv);\n enqueue(s, old.command, old.inv.params, old.resolve, old.reject);\n }\n }\n const ctxAligned =\n s.ctxPending.every((c, i) => c.seq === ctxWm + 1 + i) &&\n s.record.ctxNextSeq === ctxWm + s.ctxPending.length + 1;\n if (!ctxAligned) {\n setRecord(s, { ...s.record, ctxNextSeq: ctxWm + 1 });\n restateContext(s);\n }\n};\n\nconst handleHello = (\n s: Session,\n { ops, syn }: { ops: unknown[]; syn: StatewireSyn },\n) => {\n const record = syn.record ?? \"fresh\";\n if (!RECORDS.includes(record)) {\n protocolError(`unknown record: ${String(record)}`);\n }\n ingestOps(s, ops);\n s.attach = \"attached\";\n s.attachEpoch += 1;\n s.frameFailures = 0;\n s.resumeAt = 0;\n s.ephemeral = record === \"ephemeral\";\n if (s.ephemeral) s.deactivated = true;\n const [attachCmd, attachCtx] = s.attachLaneSeqs;\n s.attachLaneSeqs = [new Set(), new Set()];\n if (record === \"resumed\") {\n // Resending unsettled statements is exactly-once-safe: absorbed replays\n // re-emit their stored verdicts (applied while still in flight).\n for (const entry of s.pending) {\n entry.posted = attachCmd.has(entry.seq);\n }\n const ctxWatermark = syn.ctxSeq ?? 0;\n s.ctxPending = s.ctxPending.filter((c) => c.seq > ctxWatermark);\n for (const c of s.ctxPending) c.posted = attachCtx.has(c.seq);\n snapClocks(s, syn.seq ?? 0, ctxWatermark);\n syncBacklog(s);\n } else {\n resetRecord(s, s.record.clientId);\n }\n s.sessionKnown = true;\n publishIngested(s);\n void pump(s);\n};\n\nconst handleOps = (\n s: Session,\n { ops, cmds }: { ops: unknown[]; cmds: StatewireCmd[] },\n) => {\n ingestOps(s, ops);\n for (const cmd of cmds) handleCmd(s, cmd);\n syncBacklog(s);\n publishIngested(s);\n};\n\n// A {syn} refusal never settles: reconcile the resend queues against the\n// stated watermarks; retry present means wait, absent means resend now.\nconst handleRefusal = (s: Session, syn: StatewireSyn) => {\n const { seq, ctxSeq } = syn;\n if (typeof seq === \"number\") {\n const watermark = Math.max(seq, s.durableSeq);\n for (const entry of s.pending) entry.posted = entry.seq <= watermark;\n }\n if (typeof ctxSeq === \"number\") {\n s.ctxPending = s.ctxPending.filter((c) => c.seq > ctxSeq);\n for (const c of s.ctxPending) c.posted = false;\n }\n if (typeof syn.retry === \"number\") {\n s.resumeAt = Date.now() + syn.retry;\n }\n const firstCmd = s.pending.find((p) => !p.posted);\n const firstCtx = s.ctxPending.find((c) => !c.posted);\n const clocksAhead =\n (typeof syn.seq === \"number\" &&\n firstCmd !== undefined &&\n firstCmd.seq > Math.max(syn.seq, s.durableSeq) + 1) ||\n (typeof syn.ctxSeq === \"number\" &&\n firstCtx !== undefined &&\n firstCtx.seq > syn.ctxSeq + 1);\n // Clocks ahead of a record that cannot admit them: reattach first and let\n // the hello's record arbitrate.\n if (clocksAhead) s.transport?.connection.reconnect();\n wake(s);\n void pump(s);\n};\n\nconst handleDetach = (s: Session) => {\n if (s.attach === \"attached\") s.attach = \"detached\";\n if (s.ephemeral) ephemeralDetach(s);\n wake(s);\n};\n\nconst handleFinish = (s: Session, { reason }: { reason: string }) => {\n if (reason === \"evicted\" || reason === \"error\") return;\n s.attach = \"finished\";\n settleAll(s, reason === \"superseded\" ? \"superseded\" : \"gone\");\n publish(s);\n wake(s);\n};\n\nconst isFinished = (s: Session) => s.attach === \"finished\";\n\ntype Lanes = {\n frame: StatewireFrame;\n cmdEntries: Pending[];\n ctxEntries: CtxPending[];\n};\n\n// A lane must be strictly consecutive within a frame: the first unposted run\n// per lane rides now, later runs ride follow-up frames.\nconst buildLanes = (s: Session): Lanes | null => {\n const ctxEntries: CtxPending[] = [];\n for (const entry of s.ctxPending) {\n if (entry.posted) continue;\n if (ctxEntries.length > 0 && entry.seq !== ctxEntries.at(-1)!.seq + 1)\n break;\n ctxEntries.push(entry);\n }\n const cmdEntries: Pending[] = [];\n for (const entry of s.pending) {\n if (entry.posted) continue;\n if (cmdEntries.length > 0 && entry.seq !== cmdEntries.at(-1)!.seq + 1)\n break;\n cmdEntries.push(entry);\n }\n if (ctxEntries.length === 0 && cmdEntries.length === 0) return null;\n return {\n frame: {\n ...(ctxEntries.length > 0 && {\n ctx: ctxEntries.map((c) => ({ ops: c.ops, seq: c.seq })),\n }),\n ...(cmdEntries.length > 0 && {\n cmd: cmdEntries.map((p) => ({ ...p.command, seq: p.seq })),\n }),\n },\n cmdEntries,\n ctxEntries,\n };\n};\n\nconst takeAttachLanes = (s: Session): StatewireFrame | null => {\n s.attachLaneSeqs = [new Set(), new Set()];\n const wanted = s.lanesOnAttach;\n s.lanesOnAttach = false;\n if (!wanted || !s.sessionKnown) return null;\n const lanes = buildLanes(s);\n if (!lanes) return null;\n s.attachLaneSeqs = [\n new Set(lanes.cmdEntries.map((p) => p.seq)),\n new Set(lanes.ctxEntries.map((c) => c.seq)),\n ];\n for (const entry of lanes.cmdEntries) entry.attempted = true;\n return lanes.frame;\n};\n\nconst pump = async (s: Session) => {\n if (s.pumping) return;\n s.pumping = true;\n try {\n while (s.running && !isFinished(s)) {\n const transport = s.transport;\n if (buildLanes(s) === null) return;\n if (s.attach !== \"attached\" || !transport) {\n if (transport?.connection.status === \"idle\") {\n transport.connection.reconnect();\n }\n await waitForWake(s);\n continue;\n }\n const resumeDelay = s.resumeAt - Date.now();\n if (resumeDelay > 0) {\n await sleepUntilWake(s, resumeDelay);\n continue;\n }\n const lanes = buildLanes(s);\n if (!lanes) continue;\n const epoch = s.attachEpoch;\n for (const entry of lanes.cmdEntries) entry.attempted = true;\n let result;\n try {\n result = await transport.send(lanes.frame);\n } catch {\n if (!s.running || isFinished(s)) return;\n s.frameFailures += 1;\n await sleepUntilWake(s, backoff(s.frameFailures));\n continue;\n }\n if (!s.running) return;\n s.frameFailures = 0;\n if (result.type === \"ok\") {\n if (epoch === s.attachEpoch) {\n for (const entry of lanes.cmdEntries) {\n if (s.pending.includes(entry)) {\n entry.posted = true;\n s.lifecycle?.onPosted?.(entry.inv);\n }\n }\n for (const entry of lanes.ctxEntries) entry.posted = true;\n }\n } else if (result.type === \"refusal\") {\n if (epoch === s.attachEpoch) handleRefusal(s, result.syn);\n } else if (result.type === \"stale-lease\") {\n // A POST outlived its attach: reattach first, the hello arbitrates.\n if (epoch === s.attachEpoch) {\n transport.connection.reconnect();\n await waitForWake(s);\n }\n } else if (result.type === \"no-lease\") {\n // No out-of-band write channel: the frame rides a fresh attach.\n if (epoch === s.attachEpoch) {\n s.lanesOnAttach = true;\n transport.connection.reconnect();\n while (s.running && !isFinished(s) && epoch === s.attachEpoch) {\n await waitForWake(s);\n }\n }\n }\n }\n } finally {\n s.pumping = false;\n }\n};\n\nconst start = (s: Session) => {\n if (!s.recordPersisted) {\n s.recordPersisted = true;\n s.store.save(s.record);\n }\n resume(s);\n s.running = true;\n void pump(s);\n};\n\nconst stop = (s: Session) => {\n s.running = false;\n wake(s);\n settleAll(s, \"unmount\");\n};\n\nconst setTransport = (s: Session, transport: RemoteLink) => {\n if (s.transport === transport) return;\n s.transport = transport;\n wake(s);\n};\n\nconst enqueue = (\n s: Session,\n command: WireCommand,\n params: readonly unknown[],\n resolve: (payload: unknown) => void,\n reject: (error: StatewireSendError) => void,\n) => {\n const seq = s.record.nextSeq;\n const entry: Pending = {\n seq,\n command,\n inv: {\n method: command.method,\n params,\n ts: Date.now(),\n seq,\n clientId: s.record.clientId,\n },\n attempted: false,\n posted: false,\n applied: false,\n resolve,\n reject,\n };\n s.pending.push(entry);\n setRecord(s, { ...s.record, nextSeq: seq + 1, commands: backlog(s) });\n s.lifecycle?.onStart(entry.inv);\n void pump(s);\n};\n\n// A stored command cannot be told from one that reached the server: attempted\n// keeps a fresh record settling it lost instead of replaying it blind.\nconst resume = (s: Session) => {\n const stored = [...s.record.commands].sort((a, b) => a.seq - b.seq);\n for (const { seq, ...command } of stored) {\n const params =\n command.meta === undefined\n ? command.params\n : (reviveValue([...command.params], command.meta) as unknown[]);\n const entry: Pending = {\n seq,\n command,\n inv: {\n method: command.method,\n params,\n ts: Date.now(),\n seq,\n clientId: s.record.clientId,\n },\n attempted: true,\n posted: false,\n applied: false,\n resolve: () => {},\n reject: () => {},\n };\n s.pending.push(entry);\n s.lifecycle?.onStart(entry.inv);\n }\n};\n\nconst sendCommand = (\n s: Session,\n method: string,\n params: readonly unknown[],\n): Promise<unknown> => {\n if (!s.running) {\n return rejectedSend({ fate: \"aborted\", cause: \"unmount\" });\n }\n if (isFinished(s)) {\n return rejectedSend({ fate: \"aborted\", cause: \"stream-gone\" });\n }\n const result = new Promise<unknown>((resolve, reject) => {\n const { params: encoded, meta } = encodeParams(params);\n enqueue(\n s,\n { method, params: encoded, ...(meta !== undefined && { meta }) },\n params,\n resolve,\n reject,\n );\n });\n result.catch(() => {});\n return result;\n};\n\nconst setContext = (s: Session, value: Record<string, unknown>): void => {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new Error(\"statewire: context must be a plain object\");\n }\n const { value: encoded, meta } = encodeValue(value);\n s.ctxOps = [\n {\n op: \"replace\",\n path: [],\n value: encoded,\n ...(meta !== undefined && { meta }),\n },\n ];\n const seq = s.record.ctxNextSeq;\n setRecord(s, { ...s.record, ctxNextSeq: seq + 1 });\n s.ctxPending.push({ seq, ops: s.ctxOps, posted: false });\n wake(s);\n void pump(s);\n};\n\nexport const useStatewireSession = (\n options: StatewireSessionOptions,\n): Session => {\n const [defaultStore] = useState(createMemorySessionStore);\n const mounted = useContext(SessionStorageContext);\n if (options.sessionStore !== undefined && mounted !== undefined) {\n throw new Error(\n \"statewire: session storage is set twice — drop the transport's sessionStore or the client's storage.session\",\n );\n }\n const store = options.sessionStore ?? mounted ?? defaultStore;\n const [s] = useState((): Session => {\n const stored = store.load();\n if (stored !== null && !Array.isArray(stored.commands)) {\n throw new Error(\n \"statewire: session storage loaded a record without a commands array\",\n );\n }\n const record = stored ?? {\n clientId: newClientId(),\n nextSeq: 1,\n ctxNextSeq: 1,\n commands: [],\n };\n const session: Session = {\n record,\n ctx: {\n get clientId() {\n return session.record.clientId;\n },\n takeAttachLanes: () => takeAttachLanes(session),\n onHello: (args) => handleHello(session, args),\n onOps: (args) => handleOps(session, args),\n onCmds: (cmds) => handleCmds(session, cmds),\n onRefusal: (syn) => handleRefusal(session, syn),\n onIdle: (level) => setIdle(session, level),\n onDetach: () => handleDetach(session),\n onFinish: (args) => handleFinish(session, args),\n },\n recordPersisted: stored !== null,\n store,\n ephemeral: false,\n deactivated: false,\n sessionKnown: false,\n durableSeq: 0,\n resumeAt: 0,\n idle: false,\n onDone: undefined,\n ctxOps: undefined,\n ctxPending: [],\n lanesOnAttach: false,\n attachLaneSeqs: [new Set(), new Set()],\n frameFailures: 0,\n pending: [],\n canonical: undefined,\n lastPublished: undefined,\n changed: [],\n source: {\n snapshot: () => session.canonical,\n takeChanged: () => {\n const changed = session.changed;\n session.changed = [];\n return changed;\n },\n setOnChange: (listener) => {\n session.notify = listener ?? (() => {});\n },\n },\n attach: \"detached\",\n attachEpoch: 0,\n running: false,\n pumping: false,\n transport: null,\n waiters: [],\n lifecycle: null,\n notify: () => {},\n commands: createMethodProxy((method, params) =>\n sendCommand(session, method, params),\n ),\n };\n bindCommandsHost(session.commands, {\n setListener: (listener) => {\n session.lifecycle = listener;\n },\n });\n return session;\n });\n s.store = store;\n s.onDone = options.onDone;\n useEffect(() => {\n start(s);\n return () => stop(s);\n }, [s]);\n return s;\n};\n\nexport { setContext, setTransport };\n"],"mappings":"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"}
|