statewire 0.12.2 → 0.13.1

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.
@@ -41,6 +41,7 @@ type Gate = {
41
41
  type AttemptOutcome = "stop" | "reattach" | "ended" | "idle";
42
42
  type Conn = {
43
43
  connection: StatewireClient.Connection;
44
+ parked: boolean;
44
45
  hasSnapshot: boolean;
45
46
  lease: string | null;
46
47
  attempt: number;
@@ -62,6 +63,7 @@ declare const validateRefusalSyn: (value: unknown) => StatewireSyn;
62
63
  declare const handleEnvelope: (c: Conn, value: unknown) => AttemptOutcome | null;
63
64
  declare const useStatewireConn: (props: {
64
65
  url: string;
66
+ isNew?: boolean | undefined;
65
67
  onChange?: ((connection: StatewireClient.Connection) => void) | undefined;
66
68
  ctx: StatewireRemoteProps;
67
69
  attemptOnce: (c: Conn, abort: AbortController) => Promise<AttemptOutcome>;
@@ -1 +1 @@
1
- {"version":3,"file":"connection.d.ts","names":[],"sources":["../../src/client/connection.ts"],"mappings":";;;;KAQY;EACV;EACA;;;cAIW;KAQD;EACV;EACA,2BAA2B;EAC3B;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;cA8NvC,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"}
1
+ {"version":3,"file":"connection.d.ts","names":[],"sources":["../../src/client/connection.ts"],"mappings":";;;;KAQY;EACV;EACA;;;cAIW;KAQD;EACV;EACA,2BAA2B;EAC3B;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;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;cA8NvC,iBACX,GAAG,MACH,mBACC;cA6FU,mBAAoB;EAC/B;EACA;EACA,aAAa,YAAY,gBAAgB;EACzC,KAAK;EACL,cAAc,GAAG,MAAM,OAAO,oBAAoB,QAAQ;MACxD;cAuDS,WAAY,SAAS,gBAAW"}
@@ -271,6 +271,11 @@ const reconnectConn = (c) => {
271
271
  };
272
272
  const run = async (c, lifetime, gate) => {
273
273
  const alive = () => c.lifetime === lifetime;
274
+ if (c.parked) {
275
+ await waitGate(gate);
276
+ if (!alive()) return;
277
+ c.parked = false;
278
+ }
274
279
  while (alive()) {
275
280
  const abort = new AbortController();
276
281
  c.abort = abort;
@@ -306,6 +311,7 @@ const useStatewireConn = (props) => {
306
311
  const [, setConnection] = useState(null);
307
312
  const [c] = useState(() => {
308
313
  const conn = {
314
+ parked: props.isNew ?? false,
309
315
  hasSnapshot: false,
310
316
  lease: null,
311
317
  attempt: 0,
@@ -1 +1 @@
1
- {"version":3,"file":"connection.js","names":["useEffect","useRef","useState","flushTapSync","reviveOps","reviveValue","backoff","timing","ConnectionState","StatewireRemoteProps","StatewireClient","StatewireHost","Episode","cause","message","TERMINAL","const","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\n/** The terminal cmd verdict types; \"applied\" is the sole non-terminal type. */\nexport const TERMINAL = [\n \"result\",\n \"rejected\",\n \"failed\",\n \"crashed\",\n \"expired\",\n] as const;\n\nexport type StatewireCmd = {\n seq: number;\n type?: \"applied\" | (typeof TERMINAL)[number];\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?: \"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 = finish.reason === \"gone\" || (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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"}
1
+ {"version":3,"file":"connection.js","names":["useEffect","useRef","useState","flushTapSync","reviveOps","reviveValue","backoff","timing","ConnectionState","StatewireRemoteProps","StatewireClient","StatewireHost","Episode","cause","message","TERMINAL","const","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","parked","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","isNew","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\n/** The terminal cmd verdict types; \"applied\" is the sole non-terminal type. */\nexport const TERMINAL = [\n \"result\",\n \"rejected\",\n \"failed\",\n \"crashed\",\n \"expired\",\n] as const;\n\nexport type StatewireCmd = {\n seq: number;\n type?: \"applied\" | (typeof TERMINAL)[number];\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?: \"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 parked: boolean;\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 = finish.reason === \"gone\" || (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 if (c.parked) {\n await waitGate(gate);\n if (!alive()) return;\n c.parked = false;\n }\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 isNew?: boolean | undefined;\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 parked: props.isNew ?? false,\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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"}
@@ -22,6 +22,7 @@ type Core = {
22
22
  notifiedOverlayPaths: readonly StatewirePath[];
23
23
  notifiedConnection: StatewireClient.Connection | null;
24
24
  notifiedBusy: boolean;
25
+ notifiedFailed: readonly StatewireClient.FailedCommand[];
25
26
  stale: boolean;
26
27
  everAttached: boolean;
27
28
  overlaysDirty: boolean;
@@ -32,6 +33,7 @@ type Core = {
32
33
  commandsListener: CommandsListener;
33
34
  getState: () => unknown;
34
35
  getBusy: () => boolean;
36
+ getFailed: () => readonly StatewireClient.FailedCommand[];
35
37
  commands: Statewire.CommandsProxy<any>;
36
38
  subscribe: (listener: () => void) => () => void;
37
39
  };
@@ -1 +1 @@
1
- {"version":3,"file":"core.d.ts","names":[],"sources":["../../src/client/core.ts"],"mappings":";;;;;cAYa,iBAAiB,gBAAgB;KAczC;EAAY,KAAK;EAAoB;;KAErC;EACH,kBAAkB,gBAAgB;EAClC,OAAO;EACP,QAAQ,UAAU;EAClB,UAAU;EACV,SAAS,UAAU;EACnB,gBAAgB;EAChB;EACA;EACA,uBAAuB;EACvB;EACA,+BAA+B;EAC/B,oBAAoB,gBAAgB;EACpC;EACA;EACA;EACA;EACA;EACA;EACA,WAAW;EACX;EACA,kBAAkB;EAClB;EACA;EACA,UAAU,UAAU;EACpB,YAAY;;cAyMD,mBACX,kBAAkB,gBAAgB,mBAClC,OAAO,uBAAkB"}
1
+ {"version":3,"file":"core.d.ts","names":[],"sources":["../../src/client/core.ts"],"mappings":";;;;;cAYa,iBAAiB,gBAAgB;KAgBzC;EAAY,KAAK;EAAoB;;KAErC;EACH,kBAAkB,gBAAgB;EAClC,OAAO;EACP,QAAQ,UAAU;EAClB,UAAU;EACV,SAAS,UAAU;EACnB,gBAAgB;EAChB;EACA;EACA,uBAAuB;EACvB;EACA,+BAA+B;EAC/B,oBAAoB,gBAAgB;EACpC;EACA,yBAAyB,gBAAgB;EACzC;EACA;EACA;EACA;EACA;EACA,WAAW;EACX;EACA,kBAAkB;EAClB;EACA;EACA,0BAA0B,gBAAgB;EAC1C,UAAU,UAAU;EACpB,YAAY;;cA+ND,mBACX,kBAAkB,gBAAgB,mBAClC,OAAO,uBAAkB"}
@@ -10,6 +10,7 @@ const LIVE_CONNECTION = {
10
10
  status: "live",
11
11
  reconnect: () => {}
12
12
  };
13
+ const NO_FAILED = [];
13
14
  const failureOf = (error) => error instanceof StatewireSendError ? error.failure : {
14
15
  fate: "rejected",
15
16
  cause: "server",
@@ -45,11 +46,18 @@ const drainCanonical = (c) => {
45
46
  }
46
47
  }
47
48
  };
49
+ const dropCovered = (c) => {
50
+ const { failureCovered } = c.getOptions();
51
+ const failures = c.value?.failures;
52
+ if (!failureCovered || !failures || c.canonical === void 0) return;
53
+ for (const entry of failures.list()) if (failureCovered(c.canonical, entry)) failures.discard(entry);
54
+ };
48
55
  const recompute = (c) => {
49
56
  if (!c.stale || !c.value) return;
50
57
  c.stale = false;
51
58
  const prevCanonical = c.canonical;
52
59
  drainCanonical(c);
60
+ if (c.canonical !== prevCanonical) dropCovered(c);
53
61
  if (c.canonical === prevCanonical && !c.overlaysDirty) return;
54
62
  c.overlaysDirty = false;
55
63
  const { optimistic, materialize } = c.getOptions();
@@ -86,7 +94,7 @@ const recompute = (c) => {
86
94
  c.overlayPaths = paths;
87
95
  };
88
96
  const getConnection = (c) => c.value?.connection ?? LIVE_CONNECTION;
89
- const flush = (c) => {
97
+ const flush = (c, scheduled = false) => {
90
98
  if (!c.value) return;
91
99
  if (c.flushing) {
92
100
  schedule(c);
@@ -121,7 +129,15 @@ const flush = (c) => {
121
129
  const busy = c.getBusy();
122
130
  const busyChanged = busy !== c.notifiedBusy;
123
131
  c.notifiedBusy = busy;
124
- if (stateChanged || connectionChanged || busyChanged) for (const listener of [...c.listeners]) listener();
132
+ const failed = c.value.failures?.list() ?? NO_FAILED;
133
+ let failedChanged = false;
134
+ if (failed !== c.notifiedFailed) {
135
+ if (scheduled) {
136
+ c.notifiedFailed = failed;
137
+ failedChanged = true;
138
+ } else schedule(c);
139
+ }
140
+ if (stateChanged || connectionChanged || busyChanged || failedChanged) for (const listener of [...c.listeners]) listener();
125
141
  } finally {
126
142
  c.flushing = false;
127
143
  }
@@ -185,6 +201,7 @@ const useStatewireCore = (getOptions, value) => {
185
201
  notifiedOverlayPaths: [],
186
202
  notifiedConnection: null,
187
203
  notifiedBusy: true,
204
+ notifiedFailed: NO_FAILED,
188
205
  stale: false,
189
206
  everAttached: false,
190
207
  overlaysDirty: false,
@@ -193,7 +210,7 @@ const useStatewireCore = (getOptions, value) => {
193
210
  listeners: /* @__PURE__ */ new Set(),
194
211
  post: createMacrotaskPoster(() => {
195
212
  c.scheduled = false;
196
- flush(c);
213
+ flush(c, true);
197
214
  }),
198
215
  commandsListener: {
199
216
  onStart: (inv) => {
@@ -223,6 +240,7 @@ const useStatewireCore = (getOptions, value) => {
223
240
  return c.published;
224
241
  },
225
242
  getBusy: () => c.value?.isBusy ?? true,
243
+ getFailed: () => c.notifiedFailed,
226
244
  commands: createMethodProxy((method, params) => {
227
245
  if (!c.value) return Promise.reject(new StatewireSendError({
228
246
  fate: "aborted",
@@ -1 +1 @@
1
- {"version":3,"file":"core.js","names":["useEffect","useState","createChanges","getCommandsHost","getStateHost","CommandsInvocation","CommandsListener","recordMutations","createMacrotaskPoster","createMethodProxy","StatewirePath","StatewireSendError","Statewire","StatewireClient","LIVE_CONNECTION","Connection","status","reconnect","failureOf","error","SendFailure","failure","fate","cause","message","Error","String","Overlay","inv","dropped","Core","getOptions","Options","value","source","StateSource","overlays","updates","CommandUpdate","pendingChanged","canonical","published","overlayPaths","notifiedPublished","notifiedOverlayPaths","notifiedConnection","notifiedBusy","stale","everAttached","overlaysDirty","flushing","scheduled","listeners","Set","post","commandsListener","getState","getBusy","commands","CommandsProxy","subscribe","listener","schedule","c","markStale","drainCanonical","push","takeChanged","snapshot","undefined","next","initialState","state","recompute","prevCanonical","optimistic","materialize","paths","overlay","ctx","OptimisticContext","ts","seq","clientId","recorded","draft","method","params","Command","changedPaths","console","getConnection","connection","flush","options","stateChanged","prev","changed","onStateChange","changes","length","batch","update","onCommandChange","connectionChanged","onConnectionChange","busy","busyChanged","note","extra","result","attach","nextSource","setOnChange","setListener","detach","useStatewireCore","core","onStart","onPosted","onApplied","onCovered","some","o","filter","onSettle","outcome","type","payload","isBusy","Promise","reject","send","Record","args","promise","add","delete"],"sources":["../../src/client/core.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { createChanges } from \"./changes\";\nimport { getCommandsHost, getStateHost } from \"../host/host\";\nimport type { CommandsInvocation, CommandsListener } from \"../host/host\";\nimport { recordMutations } from \"../util/recordMutations\";\nimport { createMacrotaskPoster } from \"../util/macrotask\";\nimport { createMethodProxy } from \"../util/methodProxy\";\nimport type { StatewirePath } from \"../wire/ops\";\nimport { StatewireSendError } from \"../types\";\nimport type { Statewire } from \"../types\";\nimport type { StatewireClient } from \"../statewire\";\n\nexport const LIVE_CONNECTION: StatewireClient.Connection = {\n status: \"live\",\n reconnect: () => {},\n};\n\nconst failureOf = (error: unknown): StatewireClient.SendFailure =>\n error instanceof StatewireSendError\n ? error.failure\n : {\n fate: \"rejected\",\n cause: \"server\",\n message: error instanceof Error ? error.message : String(error),\n };\n\ntype Overlay = { inv: CommandsInvocation; dropped: boolean };\n\ntype Core = {\n getOptions: () => StatewireClient.Options<any, any>;\n value: Statewire<unknown> | null;\n source: Statewire.StateSource | null;\n overlays: Overlay[];\n updates: Statewire.CommandUpdate[];\n pendingChanged: StatewirePath[];\n canonical: unknown;\n published: unknown;\n overlayPaths: readonly StatewirePath[];\n notifiedPublished: unknown;\n notifiedOverlayPaths: readonly StatewirePath[];\n notifiedConnection: StatewireClient.Connection | null;\n notifiedBusy: boolean;\n stale: boolean;\n everAttached: boolean;\n overlaysDirty: boolean;\n flushing: boolean;\n scheduled: boolean;\n listeners: Set<() => void>;\n post: () => void;\n commandsListener: CommandsListener;\n getState: () => unknown;\n getBusy: () => boolean;\n commands: Statewire.CommandsProxy<any>;\n subscribe: (listener: () => void) => () => void;\n};\n\nconst schedule = (c: Core) => {\n if (c.scheduled) return;\n c.scheduled = true;\n c.post();\n};\n\nconst markStale = (c: Core) => {\n c.stale = true;\n schedule(c);\n};\n\n// initialState stands in for the canonical state only before the first\n// attach snapshot; replay boundaries after that read as loading, not preload.\nconst drainCanonical = (c: Core) => {\n if (c.source) {\n c.pendingChanged.push(...c.source.takeChanged());\n const snapshot = c.source.snapshot();\n if (snapshot !== undefined) {\n c.everAttached = true;\n c.canonical = snapshot;\n return;\n }\n const next = c.everAttached ? undefined : c.getOptions().initialState;\n if (next !== c.canonical) c.pendingChanged.push([]);\n c.canonical = next;\n } else if (c.value) {\n if (c.value.state !== undefined) c.everAttached = true;\n const next = c.everAttached ? c.value.state : c.getOptions().initialState;\n if (next !== c.canonical) {\n c.pendingChanged.push([]);\n c.canonical = next;\n }\n }\n};\n\nconst recompute = (c: Core) => {\n if (!c.stale || !c.value) return;\n c.stale = false;\n const prevCanonical = c.canonical;\n drainCanonical(c);\n if (c.canonical === prevCanonical && !c.overlaysDirty) return;\n c.overlaysDirty = false;\n const { optimistic, materialize } = c.getOptions();\n let next = c.canonical;\n let paths: readonly StatewirePath[] = [];\n if (optimistic && c.canonical !== undefined) {\n for (const overlay of c.overlays) {\n if (overlay.dropped) continue;\n const { inv } = overlay;\n const ctx: StatewireClient.OptimisticContext = {\n ts: inv.ts,\n seq: inv.seq,\n ...(inv.clientId !== undefined && { clientId: inv.clientId }),\n };\n try {\n const recorded = recordMutations(next, (draft) =>\n optimistic(\n draft as never,\n { method: inv.method, params: inv.params } as Statewire.Command,\n ctx,\n ),\n );\n next = recorded.state;\n paths = [...paths, ...recorded.changedPaths];\n } catch (error) {\n overlay.dropped = true;\n console.error(\n `statewire: optimistic reducer threw for \"${inv.method}\"; dropping its optimistic update`,\n error,\n );\n }\n }\n }\n if (materialize && next !== undefined) {\n try {\n const recorded = recordMutations(next, (draft) =>\n materialize(draft as never),\n );\n next = recorded.state;\n paths = [...paths, ...recorded.changedPaths];\n } catch (error) {\n console.error(\n \"statewire: materialize threw; publishing without its update\",\n error,\n );\n }\n }\n c.published = next;\n c.overlayPaths = paths;\n};\n\nconst getConnection = (c: Core): StatewireClient.Connection =>\n c.value?.connection ?? LIVE_CONNECTION;\n\nconst flush = (c: Core) => {\n if (!c.value) return;\n if (c.flushing) {\n schedule(c);\n return;\n }\n c.flushing = true;\n try {\n recompute(c);\n const options = c.getOptions();\n const stateChanged = c.published !== c.notifiedPublished;\n if (stateChanged) {\n const prev = c.notifiedPublished;\n const changed = [\n ...c.pendingChanged,\n ...c.notifiedOverlayPaths,\n ...c.overlayPaths,\n ];\n c.pendingChanged = [];\n c.notifiedPublished = c.published;\n c.notifiedOverlayPaths = c.overlayPaths;\n options.onStateChange?.(c.published as never, {\n changes: createChanges(prev, c.published, changed),\n });\n }\n if (c.updates.length > 0 && c.published !== undefined) {\n const batch = c.updates;\n c.updates = [];\n for (const update of batch) {\n options.onCommandChange?.(update, { state: c.published as never });\n }\n }\n const connection = getConnection(c);\n const connectionChanged = connection !== c.notifiedConnection;\n c.notifiedConnection = connection;\n if (connectionChanged) options.onConnectionChange?.(connection);\n const busy = c.getBusy();\n const busyChanged = busy !== c.notifiedBusy;\n c.notifiedBusy = busy;\n if (stateChanged || connectionChanged || busyChanged) {\n for (const listener of [...c.listeners]) listener();\n }\n } finally {\n c.flushing = false;\n }\n};\n\nconst note = (\n c: Core,\n inv: CommandsInvocation,\n status: Statewire.CommandUpdate[\"status\"],\n extra?: { result?: unknown; failure?: StatewireClient.SendFailure },\n) => {\n if (!c.getOptions().onCommandChange) return;\n c.updates.push({\n method: inv.method,\n params: inv.params as never[],\n seq: inv.seq,\n status,\n ...extra,\n });\n schedule(c);\n};\n\nconst attach = (c: Core, next: Statewire<unknown>) => {\n if (next === c.value) return;\n const prev = c.value;\n c.value = next;\n const nextSource =\n next.source ??\n (typeof next.state === \"object\" && next.state !== null\n ? getStateHost(next.state)\n : undefined) ??\n null;\n if (nextSource !== c.source) {\n c.source?.setOnChange(null);\n c.source = nextSource;\n c.source?.setOnChange(() => markStale(c));\n // A new source is a new attach lifecycle: initialState preloads again.\n c.everAttached = false;\n markStale(c);\n }\n if (next.commands !== prev?.commands) {\n if (prev) getCommandsHost(prev.commands)?.setListener(null);\n getCommandsHost(next.commands)?.setListener(c.commandsListener);\n if (c.overlays.length > 0) {\n c.overlays = [];\n c.overlaysDirty = true;\n }\n markStale(c);\n }\n if (!c.source) markStale(c);\n schedule(c);\n};\n\nconst detach = (c: Core) => {\n c.source?.setOnChange(null);\n c.source = null;\n if (c.value) {\n getCommandsHost(c.value.commands)?.setListener(null);\n c.value = null;\n }\n};\n\nexport const useStatewireCore = (\n getOptions: () => StatewireClient.Options<any, any>,\n value: Statewire<unknown>,\n) => {\n const [core] = useState((): Core => {\n const c: Core = {\n getOptions,\n value: null,\n source: null,\n overlays: [],\n updates: [],\n pendingChanged: [],\n canonical: undefined,\n published: undefined,\n overlayPaths: [],\n notifiedPublished: undefined,\n notifiedOverlayPaths: [],\n notifiedConnection: null,\n notifiedBusy: true,\n stale: false,\n everAttached: false,\n overlaysDirty: false,\n flushing: false,\n scheduled: false,\n listeners: new Set(),\n post: createMacrotaskPoster(() => {\n c.scheduled = false;\n flush(c);\n }),\n commandsListener: {\n onStart: (inv) => {\n c.overlays.push({ inv, dropped: false });\n c.overlaysDirty = true;\n markStale(c);\n note(c, inv, \"queued\");\n },\n onPosted: (inv) => note(c, inv, \"posted\"),\n onApplied: (inv) => note(c, inv, \"applied\"),\n onCovered: (inv) => {\n if (!c.overlays.some((o) => o.inv === inv)) return;\n c.overlays = c.overlays.filter((o) => o.inv !== inv);\n c.overlaysDirty = true;\n markStale(c);\n },\n onSettle: (inv, outcome) => {\n note(\n c,\n inv,\n \"settled\",\n outcome.type === \"accepted\"\n ? { result: outcome.payload }\n : { failure: failureOf(outcome.error) },\n );\n flush(c);\n },\n },\n getState: () => {\n recompute(c);\n return c.published;\n },\n getBusy: () => c.value?.isBusy ?? true,\n commands: createMethodProxy<Statewire.CommandsProxy<any>>(\n (method, params) => {\n if (!c.value) {\n return Promise.reject(\n new StatewireSendError({ fate: \"aborted\", cause: \"unmount\" }),\n );\n }\n const send = (\n c.value.commands as Record<\n string,\n (...args: unknown[]) => Promise<unknown>\n >\n )[method]!;\n const promise = send(...params);\n flush(c);\n return promise;\n },\n ),\n subscribe: (listener) => {\n c.listeners.add(listener);\n return () => {\n c.listeners.delete(listener);\n };\n },\n };\n return c;\n });\n core.getOptions = getOptions;\n // Attach during render so the first snapshot reads the transport's state;\n // re-attaching the same transport is a no-op and the single-slot host\n // listeners make a swap land on the last writer.\n attach(core, value);\n useEffect(() => {\n attach(core, value);\n });\n useEffect(() => () => detach(core), [core]);\n return core;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"core.js","names":["useEffect","useState","createChanges","getCommandsHost","getStateHost","CommandsInvocation","CommandsListener","recordMutations","createMacrotaskPoster","createMethodProxy","StatewirePath","StatewireSendError","Statewire","StatewireClient","LIVE_CONNECTION","Connection","status","reconnect","NO_FAILED","FailedCommand","failureOf","error","SendFailure","failure","fate","cause","message","Error","String","Overlay","inv","dropped","Core","getOptions","Options","value","source","StateSource","overlays","updates","CommandUpdate","pendingChanged","canonical","published","overlayPaths","notifiedPublished","notifiedOverlayPaths","notifiedConnection","notifiedBusy","notifiedFailed","stale","everAttached","overlaysDirty","flushing","scheduled","listeners","Set","post","commandsListener","getState","getBusy","getFailed","commands","CommandsProxy","subscribe","listener","schedule","c","markStale","drainCanonical","push","takeChanged","snapshot","undefined","next","initialState","state","dropCovered","failureCovered","failures","entry","list","discard","recompute","prevCanonical","optimistic","materialize","paths","overlay","ctx","OptimisticContext","ts","seq","clientId","recorded","draft","method","params","Command","changedPaths","console","getConnection","connection","flush","options","stateChanged","prev","changed","onStateChange","changes","length","batch","update","onCommandChange","connectionChanged","onConnectionChange","busy","busyChanged","failed","failedChanged","note","extra","result","attach","nextSource","setOnChange","setListener","detach","useStatewireCore","core","onStart","onPosted","onApplied","onCovered","some","o","filter","onSettle","outcome","type","payload","isBusy","Promise","reject","send","Record","args","promise","add","delete"],"sources":["../../src/client/core.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { createChanges } from \"./changes\";\nimport { getCommandsHost, getStateHost } from \"../host/host\";\nimport type { CommandsInvocation, CommandsListener } from \"../host/host\";\nimport { recordMutations } from \"../util/recordMutations\";\nimport { createMacrotaskPoster } from \"../util/macrotask\";\nimport { createMethodProxy } from \"../util/methodProxy\";\nimport type { StatewirePath } from \"../wire/ops\";\nimport { StatewireSendError } from \"../types\";\nimport type { Statewire } from \"../types\";\nimport type { StatewireClient } from \"../statewire\";\n\nexport const LIVE_CONNECTION: StatewireClient.Connection = {\n status: \"live\",\n reconnect: () => {},\n};\n\nconst NO_FAILED: readonly StatewireClient.FailedCommand[] = [];\n\nconst failureOf = (error: unknown): StatewireClient.SendFailure =>\n error instanceof StatewireSendError\n ? error.failure\n : {\n fate: \"rejected\",\n cause: \"server\",\n message: error instanceof Error ? error.message : String(error),\n };\n\ntype Overlay = { inv: CommandsInvocation; dropped: boolean };\n\ntype Core = {\n getOptions: () => StatewireClient.Options<any, any>;\n value: Statewire<unknown> | null;\n source: Statewire.StateSource | null;\n overlays: Overlay[];\n updates: Statewire.CommandUpdate[];\n pendingChanged: StatewirePath[];\n canonical: unknown;\n published: unknown;\n overlayPaths: readonly StatewirePath[];\n notifiedPublished: unknown;\n notifiedOverlayPaths: readonly StatewirePath[];\n notifiedConnection: StatewireClient.Connection | null;\n notifiedBusy: boolean;\n notifiedFailed: readonly StatewireClient.FailedCommand[];\n stale: boolean;\n everAttached: boolean;\n overlaysDirty: boolean;\n flushing: boolean;\n scheduled: boolean;\n listeners: Set<() => void>;\n post: () => void;\n commandsListener: CommandsListener;\n getState: () => unknown;\n getBusy: () => boolean;\n getFailed: () => readonly StatewireClient.FailedCommand[];\n commands: Statewire.CommandsProxy<any>;\n subscribe: (listener: () => void) => () => void;\n};\n\nconst schedule = (c: Core) => {\n if (c.scheduled) return;\n c.scheduled = true;\n c.post();\n};\n\nconst markStale = (c: Core) => {\n c.stale = true;\n schedule(c);\n};\n\n// initialState stands in for the canonical state only before the first\n// attach snapshot; replay boundaries after that read as loading, not preload.\nconst drainCanonical = (c: Core) => {\n if (c.source) {\n c.pendingChanged.push(...c.source.takeChanged());\n const snapshot = c.source.snapshot();\n if (snapshot !== undefined) {\n c.everAttached = true;\n c.canonical = snapshot;\n return;\n }\n const next = c.everAttached ? undefined : c.getOptions().initialState;\n if (next !== c.canonical) c.pendingChanged.push([]);\n c.canonical = next;\n } else if (c.value) {\n if (c.value.state !== undefined) c.everAttached = true;\n const next = c.everAttached ? c.value.state : c.getOptions().initialState;\n if (next !== c.canonical) {\n c.pendingChanged.push([]);\n c.canonical = next;\n }\n }\n};\n\nconst dropCovered = (c: Core) => {\n const { failureCovered } = c.getOptions();\n const failures = c.value?.failures;\n if (!failureCovered || !failures || c.canonical === undefined) return;\n for (const entry of failures.list()) {\n if (failureCovered(c.canonical as never, entry)) failures.discard(entry);\n }\n};\n\nconst recompute = (c: Core) => {\n if (!c.stale || !c.value) return;\n c.stale = false;\n const prevCanonical = c.canonical;\n drainCanonical(c);\n if (c.canonical !== prevCanonical) dropCovered(c);\n if (c.canonical === prevCanonical && !c.overlaysDirty) return;\n c.overlaysDirty = false;\n const { optimistic, materialize } = c.getOptions();\n let next = c.canonical;\n let paths: readonly StatewirePath[] = [];\n if (optimistic && c.canonical !== undefined) {\n for (const overlay of c.overlays) {\n if (overlay.dropped) continue;\n const { inv } = overlay;\n const ctx: StatewireClient.OptimisticContext = {\n ts: inv.ts,\n seq: inv.seq,\n ...(inv.clientId !== undefined && { clientId: inv.clientId }),\n };\n try {\n const recorded = recordMutations(next, (draft) =>\n optimistic(\n draft as never,\n { method: inv.method, params: inv.params } as Statewire.Command,\n ctx,\n ),\n );\n next = recorded.state;\n paths = [...paths, ...recorded.changedPaths];\n } catch (error) {\n overlay.dropped = true;\n console.error(\n `statewire: optimistic reducer threw for \"${inv.method}\"; dropping its optimistic update`,\n error,\n );\n }\n }\n }\n if (materialize && next !== undefined) {\n try {\n const recorded = recordMutations(next, (draft) =>\n materialize(draft as never),\n );\n next = recorded.state;\n paths = [...paths, ...recorded.changedPaths];\n } catch (error) {\n console.error(\n \"statewire: materialize threw; publishing without its update\",\n error,\n );\n }\n }\n c.published = next;\n c.overlayPaths = paths;\n};\n\nconst getConnection = (c: Core): StatewireClient.Connection =>\n c.value?.connection ?? LIVE_CONNECTION;\n\nconst flush = (c: Core, scheduled = false) => {\n if (!c.value) return;\n if (c.flushing) {\n schedule(c);\n return;\n }\n c.flushing = true;\n try {\n recompute(c);\n const options = c.getOptions();\n const stateChanged = c.published !== c.notifiedPublished;\n if (stateChanged) {\n const prev = c.notifiedPublished;\n const changed = [\n ...c.pendingChanged,\n ...c.notifiedOverlayPaths,\n ...c.overlayPaths,\n ];\n c.pendingChanged = [];\n c.notifiedPublished = c.published;\n c.notifiedOverlayPaths = c.overlayPaths;\n options.onStateChange?.(c.published as never, {\n changes: createChanges(prev, c.published, changed),\n });\n }\n if (c.updates.length > 0 && c.published !== undefined) {\n const batch = c.updates;\n c.updates = [];\n for (const update of batch) {\n options.onCommandChange?.(update, { state: c.published as never });\n }\n }\n const connection = getConnection(c);\n const connectionChanged = connection !== c.notifiedConnection;\n c.notifiedConnection = connection;\n if (connectionChanged) options.onConnectionChange?.(connection);\n const busy = c.getBusy();\n const busyChanged = busy !== c.notifiedBusy;\n c.notifiedBusy = busy;\n // The failed snapshot advances only on the scheduled pass: settles land\n // mid-unmount, where a changed snapshot re-renders the unmounting tree.\n const failed = c.value.failures?.list() ?? NO_FAILED;\n let failedChanged = false;\n if (failed !== c.notifiedFailed) {\n if (scheduled) {\n c.notifiedFailed = failed;\n failedChanged = true;\n } else {\n schedule(c);\n }\n }\n if (stateChanged || connectionChanged || busyChanged || failedChanged) {\n for (const listener of [...c.listeners]) listener();\n }\n } finally {\n c.flushing = false;\n }\n};\n\nconst note = (\n c: Core,\n inv: CommandsInvocation,\n status: Statewire.CommandUpdate[\"status\"],\n extra?: { result?: unknown; failure?: StatewireClient.SendFailure },\n) => {\n if (!c.getOptions().onCommandChange) return;\n c.updates.push({\n method: inv.method,\n params: inv.params as never[],\n seq: inv.seq,\n status,\n ...extra,\n });\n schedule(c);\n};\n\nconst attach = (c: Core, next: Statewire<unknown>) => {\n if (next === c.value) return;\n const prev = c.value;\n c.value = next;\n const nextSource =\n next.source ??\n (typeof next.state === \"object\" && next.state !== null\n ? getStateHost(next.state)\n : undefined) ??\n null;\n if (nextSource !== c.source) {\n c.source?.setOnChange(null);\n c.source = nextSource;\n c.source?.setOnChange(() => markStale(c));\n // A new source is a new attach lifecycle: initialState preloads again.\n c.everAttached = false;\n markStale(c);\n }\n if (next.commands !== prev?.commands) {\n if (prev) getCommandsHost(prev.commands)?.setListener(null);\n getCommandsHost(next.commands)?.setListener(c.commandsListener);\n if (c.overlays.length > 0) {\n c.overlays = [];\n c.overlaysDirty = true;\n }\n markStale(c);\n }\n if (!c.source) markStale(c);\n schedule(c);\n};\n\nconst detach = (c: Core) => {\n c.source?.setOnChange(null);\n c.source = null;\n if (c.value) {\n getCommandsHost(c.value.commands)?.setListener(null);\n c.value = null;\n }\n};\n\nexport const useStatewireCore = (\n getOptions: () => StatewireClient.Options<any, any>,\n value: Statewire<unknown>,\n) => {\n const [core] = useState((): Core => {\n const c: Core = {\n getOptions,\n value: null,\n source: null,\n overlays: [],\n updates: [],\n pendingChanged: [],\n canonical: undefined,\n published: undefined,\n overlayPaths: [],\n notifiedPublished: undefined,\n notifiedOverlayPaths: [],\n notifiedConnection: null,\n notifiedBusy: true,\n notifiedFailed: NO_FAILED,\n stale: false,\n everAttached: false,\n overlaysDirty: false,\n flushing: false,\n scheduled: false,\n listeners: new Set(),\n post: createMacrotaskPoster(() => {\n c.scheduled = false;\n flush(c, true);\n }),\n commandsListener: {\n onStart: (inv) => {\n c.overlays.push({ inv, dropped: false });\n c.overlaysDirty = true;\n markStale(c);\n note(c, inv, \"queued\");\n },\n onPosted: (inv) => note(c, inv, \"posted\"),\n onApplied: (inv) => note(c, inv, \"applied\"),\n onCovered: (inv) => {\n if (!c.overlays.some((o) => o.inv === inv)) return;\n c.overlays = c.overlays.filter((o) => o.inv !== inv);\n c.overlaysDirty = true;\n markStale(c);\n },\n onSettle: (inv, outcome) => {\n note(\n c,\n inv,\n \"settled\",\n outcome.type === \"accepted\"\n ? { result: outcome.payload }\n : { failure: failureOf(outcome.error) },\n );\n flush(c);\n },\n },\n getState: () => {\n recompute(c);\n return c.published;\n },\n getBusy: () => c.value?.isBusy ?? true,\n getFailed: () => c.notifiedFailed,\n commands: createMethodProxy<Statewire.CommandsProxy<any>>(\n (method, params) => {\n if (!c.value) {\n return Promise.reject(\n new StatewireSendError({ fate: \"aborted\", cause: \"unmount\" }),\n );\n }\n const send = (\n c.value.commands as Record<\n string,\n (...args: unknown[]) => Promise<unknown>\n >\n )[method]!;\n const promise = send(...params);\n flush(c);\n return promise;\n },\n ),\n subscribe: (listener) => {\n c.listeners.add(listener);\n return () => {\n c.listeners.delete(listener);\n };\n },\n };\n return c;\n });\n core.getOptions = getOptions;\n // Attach during render so the first snapshot reads the transport's state;\n // re-attaching the same transport is a no-op and the single-slot host\n // listeners make a swap land on the last writer.\n attach(core, value);\n useEffect(() => {\n attach(core, value);\n });\n useEffect(() => () => detach(core), [core]);\n return core;\n};\n"],"mappings":"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"}
@@ -73,9 +73,12 @@ type Session = {
73
73
  notify: () => void;
74
74
  commands: Record<string, (...params: unknown[]) => Promise<unknown>>;
75
75
  };
76
+ declare const failedCommands: (s: Session) => readonly StatewireClient.FailedCommand[];
76
77
  declare const setTransport: (s: Session, transport: RemoteLink) => void;
78
+ declare const discardFailed: (s: Session, entry: StatewireClient.FailedCommand) => void;
79
+ declare const retryFailed: (s: Session, entry: StatewireClient.FailedCommand) => Promise<unknown>;
77
80
  declare const setContext: (s: Session, value: Record<string, unknown>) => void;
78
81
  declare const useStatewireSession: (options: StatewireSessionOptions) => Session;
79
82
  //#endregion
80
- export { StatewireSessionOptions, setContext, setTransport, useStatewireSession };
83
+ export { StatewireSessionOptions, discardFailed, failedCommands, retryFailed, setContext, setTransport, useStatewireSession };
81
84
  //# sourceMappingURL=session.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","names":[],"sources":["../../src/client/session.ts"],"mappings":";;;;;;KA6BK;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,gBAAgB,gBAAgB;EAChC;;KAGG;EACH,KAAK;EACL,QAAQ,gBAAgB;EACxB;;KAGG;EACH,QAAQ,gBAAgB;EACxB,KAAK;EACL;EACA,OAAO,gBAAgB;EACvB,QAAQ;EACR;;EAEA;EACA;EACA;EACA;EACA;EACA,eAAe,gBAAgB;EAC/B;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;;cA4oB/C,eAAgB,GAAG,SAAS,WAAW;cA8GvC,aAAc,GAAG,SAAS,OAAO;cAgC1B,sBACX,SAAS,4BACR"}
1
+ {"version":3,"file":"session.d.ts","names":[],"sources":["../../src/client/session.ts"],"mappings":";;;;;;KA6BK;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,gBAAgB,gBAAgB;EAChC;;KAGG;EACH,KAAK;EACL,QAAQ,gBAAgB;EACxB;;KAGG;EACH,QAAQ,gBAAgB;EACxB,KAAK;EACL;EACA,OAAO,gBAAgB;EACvB,QAAQ;EACR;;EAEA;EACA;EACA;EACA;EACA;EACA,eAAe,gBAAgB;EAC/B;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;;cAwHxC,iBACX,GAAG,qBACO,gBAAgB;cAikBtB,eAAgB,GAAG,SAAS,WAAW;cAmIhC,gBACX,GAAG,SACH,OAAO,gBAAgB;cAKZ,cACX,GAAG,SACH,OAAO,gBAAgB,kBACtB;cA6BG,aAAc,GAAG,SAAS,OAAO;cAgC1B,sBACX,SAAS,4BACR"}
@@ -97,6 +97,22 @@ const cover = (s, entry) => {
97
97
  s.lifecycle?.onApplied(entry.inv);
98
98
  s.lifecycle?.onCovered?.(entry.inv);
99
99
  };
100
+ const NO_FAILED = [];
101
+ const failedCommands = (s) => s.record.failed ?? NO_FAILED;
102
+ const retainFailed = (s, entry, failure) => {
103
+ setRecord(s, {
104
+ ...s.record,
105
+ failed: [...s.record.failed ?? [], {
106
+ command: entry.command.method,
107
+ params: entry.command.params,
108
+ ...entry.command.meta !== void 0 && { meta: entry.command.meta },
109
+ failure,
110
+ ts: entry.inv.ts,
111
+ seq: entry.seq
112
+ }]
113
+ });
114
+ s.notify();
115
+ };
100
116
  const settleEntry = (s, entry, outcome) => {
101
117
  s.pending = s.pending.filter((p) => p !== entry);
102
118
  if (outcome.ok) {
@@ -106,6 +122,7 @@ const settleEntry = (s, entry, outcome) => {
106
122
  payload: outcome.payload
107
123
  });
108
124
  } else {
125
+ if (!((outcome.failure.cause === "unmount" || outcome.failure.cause === "stream-gone") && entry.seq > s.durableSeq)) retainFailed(s, entry, outcome.failure);
109
126
  entry.reject(new StatewireSendError(outcome.failure));
110
127
  s.lifecycle?.onSettle(entry.inv, {
111
128
  type: "rejected",
@@ -173,7 +190,12 @@ const rebaseRecord = (s, adopt) => {
173
190
  settleLost(s, held.filter((p) => p.attempted || p.applied));
174
191
  s.pending = [];
175
192
  s.durableSeq = 0;
193
+ const failed = s.record.failed;
176
194
  adopt();
195
+ if (failed !== void 0 && s.record.failed !== failed) setRecord(s, {
196
+ ...s.record,
197
+ failed: [...s.record.failed ?? [], ...failed]
198
+ });
177
199
  for (const old of survivors) {
178
200
  s.lifecycle?.onCovered?.(old.inv);
179
201
  enqueueInner(s, old.command, old.inv.params, old.resolve, old.reject);
@@ -321,7 +343,8 @@ const sessionLostInner = (s, seqWm, ctxWm) => {
321
343
  clientId: s.record.clientId,
322
344
  nextSeq: seqWm + 1,
323
345
  ctxNextSeq: ctxWm + 1,
324
- commands: []
346
+ commands: [],
347
+ ...s.record.failed !== void 0 && { failed: s.record.failed }
325
348
  });
326
349
  restateContext(s);
327
350
  if (held.length > 0 && s.onSessionLost?.(held.map((p) => p.inv)) === "requeue") for (const old of held) enqueueInner(s, old.command, old.inv.params, () => {}, () => {});
@@ -461,7 +484,7 @@ const pump = async (s) => {
461
484
  const transport = s.transport;
462
485
  if (buildLanes(s) === null) return;
463
486
  if (s.attach !== "attached" || !transport) {
464
- if (transport?.connection.status === "idle") transport.connection.reconnect();
487
+ if (transport?.connection.status === "idle" || transport?.parked && s.pending.length > 0) transport.connection.reconnect();
465
488
  await waitForWake(s);
466
489
  continue;
467
490
  }
@@ -578,10 +601,14 @@ const enqueueInner = (s, command, params, resolve, reject) => {
578
601
  const enqueue = (s, command, params, resolve, reject) => {
579
602
  if (!s.shared) {
580
603
  enqueueInner(s, command, params, resolve, reject);
604
+ wake(s);
581
605
  pump(s);
582
606
  return;
583
607
  }
584
- transact(s, () => enqueueInner(s, command, params, resolve, reject)).then(() => void pump(s));
608
+ transact(s, () => enqueueInner(s, command, params, resolve, reject)).then(() => {
609
+ wake(s);
610
+ pump(s);
611
+ });
585
612
  };
586
613
  const adoptStored = (s) => {
587
614
  const known = new Set(s.pending.map((p) => p.seq));
@@ -630,6 +657,41 @@ const sendCommand = (s, method, params) => {
630
657
  result.catch(() => {});
631
658
  return result;
632
659
  };
660
+ const takeFailed = (s, entry) => {
661
+ const failed = s.record.failed ?? [];
662
+ const found = failed.find((f) => f.seq === entry.seq && f.ts === entry.ts && f.command === entry.command);
663
+ if (found === void 0) throw new Error(`statewire: unknown failed command "${entry.command}"`);
664
+ setRecord(s, {
665
+ ...s.record,
666
+ failed: failed.filter((f) => f !== found)
667
+ });
668
+ s.notify();
669
+ return found;
670
+ };
671
+ const discardFailed = (s, entry) => {
672
+ takeFailed(s, entry);
673
+ };
674
+ const retryFailed = (s, entry) => {
675
+ if (!s.running) return rejectedSend({
676
+ fate: "aborted",
677
+ cause: "unmount"
678
+ });
679
+ if (isFinished(s)) return rejectedSend({
680
+ fate: "aborted",
681
+ cause: "stream-gone"
682
+ });
683
+ const found = takeFailed(s, entry);
684
+ const params = found.meta === void 0 ? found.params : reviveValue([...found.params], found.meta);
685
+ const result = new Promise((resolve, reject) => {
686
+ enqueue(s, {
687
+ method: found.command,
688
+ params: [...found.params],
689
+ ...found.meta !== void 0 && { meta: found.meta }
690
+ }, params, resolve, reject);
691
+ });
692
+ result.catch(() => {});
693
+ return result;
694
+ };
633
695
  const setContext = (s, value) => {
634
696
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("statewire: context must be a plain object");
635
697
  if (!s.running || isFinished(s)) return;
@@ -754,6 +816,6 @@ const useStatewireSession = (options) => {
754
816
  return s;
755
817
  };
756
818
  //#endregion
757
- export { setContext, setTransport, useStatewireSession };
819
+ export { discardFailed, failedCommands, retryFailed, setContext, setTransport, useStatewireSession };
758
820
 
759
821
  //# sourceMappingURL=session.js.map