statewire 0.18.1 → 0.18.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/storage.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { negotiationHeaders, protocolToken } from "./wire/protocol.js";
2
- import { applyDocumentOps } from "./wire/documents.js";
3
2
  import { wrapStorageError } from "./client/storageError.js";
4
3
  import { useEffect, useMemo, useRef } from "@assistant-ui/tap/react-shim";
5
4
  import { resource } from "@assistant-ui/tap";
@@ -22,7 +21,7 @@ const isSendFailure = (value) => {
22
21
  }
23
22
  };
24
23
  const isFailedCommand = (value) => isRecord(value) && isProtocol(value["protocol"]) && isProducer(value["produced"]) && typeof value["command"] === "string" && Array.isArray(value["params"]) && isSendFailure(value["failure"]) && typeof value["ts"] === "number" && Number.isFinite(value["ts"]) && isPositiveSafeInteger(value["seq"]);
25
- const isSessionRecord = (value) => isRecord(value) && typeof value["clientId"] === "string" && value["clientId"].length > 0 && isContext(value["context"]) && isPositiveSafeInteger(value["nextSeq"]) && isPositiveSafeInteger(value["ctxNextSeq"]) && Array.isArray(value["commands"]) && value["commands"].every(isSessionCommand) && (value["verdicts"] === void 0 || Array.isArray(value["verdicts"]) && value["verdicts"].every(isSessionVerdict)) && (value["failed"] === void 0 || Array.isArray(value["failed"]) && value["failed"].every(isFailedCommand));
24
+ const isSessionRecord = (value) => isRecord(value) && typeof value["clientId"] === "string" && value["clientId"].length > 0 && isPositiveSafeInteger(value["nextSeq"]) && Array.isArray(value["commands"]) && value["commands"].every(isSessionCommand) && (value["verdicts"] === void 0 || Array.isArray(value["verdicts"]) && value["verdicts"].every(isSessionVerdict)) && (value["failed"] === void 0 || Array.isArray(value["failed"]) && value["failed"].every(isFailedCommand));
26
25
  const isProtocol = (value) => value === void 0 || typeof value === "string" && value !== "*" && protocolToken.test(value);
27
26
  const isProducer = (value) => {
28
27
  if (value === void 0) return true;
@@ -37,51 +36,13 @@ const isProducer = (value) => {
37
36
  return false;
38
37
  }
39
38
  };
40
- const isContextOperation = (value) => {
41
- if (!isRecord(value)) return false;
42
- if (value["doc"] !== void 0 && (typeof value["doc"] !== "number" || !Number.isSafeInteger(value["doc"]) || value["doc"] < 0)) return false;
43
- if (value["op"] === "mount") try {
44
- applyDocumentOps({
45
- mode: "mounted",
46
- documents: /* @__PURE__ */ new Map()
47
- }, [value], void 0, true);
48
- return true;
49
- } catch {
50
- return false;
51
- }
52
- if (value["op"] === "unmount") return true;
53
- if (![
54
- "add",
55
- "replace",
56
- "remove"
57
- ].includes(value["op"])) return false;
58
- return Array.isArray(value["path"]) && value["path"].every((part) => typeof part === "string" || typeof part === "number" && Number.isSafeInteger(part) && part >= 0) && (value["op"] === "remove" || "value" in value);
59
- };
60
- const isContext = (value) => {
61
- if (value === void 0) return true;
62
- if (!isRecord(value) || !Array.isArray(value["ops"]) || !Array.isArray(value["pending"])) return false;
63
- if (value["recover"] !== void 0 && value["recover"] !== "version-1") return false;
64
- try {
65
- const documents = applyDocumentOps({
66
- mode: "mounted",
67
- documents: /* @__PURE__ */ new Map()
68
- }, value["ops"], void 0, true);
69
- for (const entry of value["pending"]) {
70
- if (!isRecord(entry) || !isPositiveSafeInteger(entry["seq"]) || !Array.isArray(entry["ops"])) return false;
71
- if (!entry["ops"].every(isContextOperation)) return false;
72
- }
73
- return documents.mode === "mounted";
74
- } catch {
75
- return false;
76
- }
77
- };
78
39
  const decode = (raw, legacyProtocol) => {
79
40
  if (raw === null) return null;
80
41
  const parsed = JSON.parse(raw);
81
42
  if (!isSessionRecord(parsed)) throw new Error("statewire: invalid stored session record");
82
- const { statewireStorageVersion: format, savedAt: _savedAt, ...value } = parsed;
43
+ const { statewireStorageVersion: format, savedAt: _savedAt, ctxNextSeq: _ctxNextSeq, context: _context, ...value } = parsed;
83
44
  if (!isSessionRecord(value)) throw new Error("statewire: invalid stored session record");
84
- if (format !== void 0 && format !== 1) throw new Error("statewire: unsupported session storage version");
45
+ if (format !== void 0 && format !== 1 && format !== 2) throw new Error("statewire: unsupported session storage version");
85
46
  if (legacyProtocol === void 0 || format !== void 0) return value;
86
47
  const produced = {
87
48
  wire: { version: "2026-09-07" },
@@ -89,16 +50,6 @@ const decode = (raw, legacyProtocol) => {
89
50
  };
90
51
  return {
91
52
  ...value,
92
- ...value.context === void 0 && value.ctxNextSeq > 1 ? { context: {
93
- ops: [{
94
- op: "mount",
95
- protocol: legacyProtocol.name,
96
- main: true,
97
- value: {}
98
- }],
99
- pending: [],
100
- recover: "version-1"
101
- } } : {},
102
53
  commands: value.commands.map((command) => command.produced === void 0 ? {
103
54
  ...command,
104
55
  protocol: command.protocol ?? legacyProtocol.name,
@@ -125,8 +76,8 @@ const sweep = async (options, currentKey, quota = false) => {
125
76
  return;
126
77
  }
127
78
  const format = isRecord(value) ? value["statewireStorageVersion"] : void 0;
128
- if (format !== void 0 && format !== 1) return void 0;
129
- if (!isSessionRecord(value) || value.commands.length || value.failed?.length || value.context?.pending.length) return void 0;
79
+ if (format !== void 0 && format !== 1 && format !== 2) return void 0;
80
+ if (!isSessionRecord(value) || value.commands.length || value.failed?.length) return void 0;
130
81
  const savedAt = value["savedAt"] ?? value["updatedAt"];
131
82
  return typeof savedAt === "number" && Number.isFinite(savedAt) ? savedAt : void 0;
132
83
  };
@@ -294,7 +245,7 @@ const useKeyValueSessionStorage = (options) => {
294
245
  if (!isSessionRecord(record)) throw new Error("statewire: invalid session record");
295
246
  const encoded = JSON.stringify({
296
247
  ...record,
297
- statewireStorageVersion: 1,
248
+ statewireStorageVersion: 2,
298
249
  savedAt: Date.now()
299
250
  });
300
251
  state_0.record = record;
@@ -1 +1 @@
1
- {"version":3,"file":"storage.js","names":["useEffect","useMemo","useRef","wrapStorageError","resource","StatewireClient","StatewireProtocol","negotiationHeaders","protocolToken","applyDocumentOps","isRecord","value","Record","Array","isArray","isOptionalString","undefined","isPositiveSafeInteger","Number","isSafeInteger","isSessionCommand","SessionCommand","isProtocol","isProducer","isSessionVerdict","SessionVerdict","isSendFailure","SendFailure","isFailedCommand","FailedCommand","isFinite","isSessionRecord","SessionRecord","length","isContext","every","test","wireVersion","Range","applicationProtocols","Offer","isContextOperation","mode","documents","Map","includes","part","entry","decode","raw","legacyProtocol","KeyValueSessionStorage","Options","parsed","JSON","parse","Error","statewireStorageVersion","format","savedAt","_savedAt","produced","wire","version","protocols","context","ctxNextSeq","ops","op","protocol","name","main","pending","recover","commands","map","command","failed","active","WeakMap","Backend","sweep","options","currentKey","quota","backend","retention","namespace","getAllKeys","entries","key","candidate","startsWith","get","has","getItem","push","sort","a","b","now","Date","retained","quotaStart","Math","floor","index","expired","maxAgeMs","overflow","maxEntries","remove","removeItem","withUnused","isQuotaError","error","useKeyValueSessionStorage","SessionStorage","suppliedLegacyProtocol","migrate","suppliedRetention","onError","latestErrorHandler","current","reportError","legacyName","legacyVersion","retain","configured","storageKey","state","Promise","cause","record","ready","queue","resolve","failure","asynchronous","hydrated","then","catch","set","count","delete","load","save","encoded","stringify","recoverQuota","setItem","write","result","finally","flush","action"],"sources":["../src/storage.ts"],"sourcesContent":["import { useEffect, useMemo, useRef } from \"react\";\nimport { wrapStorageError } from \"./client/storageError\";\nimport { resource } from \"@assistant-ui/tap\";\nimport type { StatewireClient } from \"./statewire\";\nimport type { StatewireProtocol } from \"./wire/protocol\";\nimport { negotiationHeaders, protocolToken } from \"./wire/protocol\";\nimport { applyDocumentOps } from \"./wire/documents\";\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isOptionalString = (value: unknown): boolean =>\n value === undefined || typeof value === \"string\";\n\nconst isPositiveSafeInteger = (value: unknown): value is number =>\n typeof value === \"number\" && Number.isSafeInteger(value) && value > 0;\n\nconst isSessionCommand = (\n value: unknown,\n): value is StatewireClient.SessionCommand =>\n isRecord(value) &&\n isPositiveSafeInteger(value[\"seq\"]) &&\n isProtocol(value[\"protocol\"]) &&\n isProducer(value[\"produced\"]) &&\n typeof value[\"method\"] === \"string\" &&\n Array.isArray(value[\"params\"]);\n\nconst isSessionVerdict = (\n value: unknown,\n): value is StatewireClient.SessionVerdict =>\n isRecord(value) &&\n isPositiveSafeInteger(value[\"seq\"]) &&\n (value[\"type\"] === \"result\" ||\n value[\"type\"] === \"rejected\" ||\n value[\"type\"] === \"failed\" ||\n value[\"type\"] === \"crashed\" ||\n value[\"type\"] === \"expired\") &&\n isOptionalString(value[\"code\"]) &&\n isOptionalString(value[\"message\"]);\n\nconst isSendFailure = (\n value: unknown,\n): value is StatewireClient.SendFailure => {\n if (!isRecord(value)) return false;\n switch (value[\"fate\"]) {\n case \"rejected\":\n return (\n value[\"cause\"] === \"server\" &&\n isOptionalString(value[\"code\"]) &&\n isOptionalString(value[\"message\"])\n );\n case \"failed\":\n return value[\"cause\"] === \"server\" && isOptionalString(value[\"message\"]);\n case \"crashed\":\n case \"expired\":\n return value[\"cause\"] === \"server\";\n case \"aborted\":\n return (\n (value[\"cause\"] === \"unmount\" ||\n value[\"cause\"] === \"stream-gone\" ||\n value[\"cause\"] === \"session-lost\") &&\n (value[\"predecessorLost\"] === undefined ||\n value[\"predecessorLost\"] === true)\n );\n case \"lost\":\n return (\n value[\"cause\"] === \"unmount\" ||\n value[\"cause\"] === \"stream-gone\" ||\n value[\"cause\"] === \"session-lost\"\n );\n default:\n return false;\n }\n};\n\nconst isFailedCommand = (\n value: unknown,\n): value is StatewireClient.FailedCommand =>\n isRecord(value) &&\n isProtocol(value[\"protocol\"]) &&\n isProducer(value[\"produced\"]) &&\n typeof value[\"command\"] === \"string\" &&\n Array.isArray(value[\"params\"]) &&\n isSendFailure(value[\"failure\"]) &&\n typeof value[\"ts\"] === \"number\" &&\n Number.isFinite(value[\"ts\"]) &&\n isPositiveSafeInteger(value[\"seq\"]);\n\nconst isSessionRecord = (\n value: unknown,\n): value is StatewireClient.SessionRecord =>\n isRecord(value) &&\n typeof value[\"clientId\"] === \"string\" &&\n value[\"clientId\"].length > 0 &&\n isContext(value[\"context\"]) &&\n isPositiveSafeInteger(value[\"nextSeq\"]) &&\n isPositiveSafeInteger(value[\"ctxNextSeq\"]) &&\n Array.isArray(value[\"commands\"]) &&\n value[\"commands\"].every(isSessionCommand) &&\n (value[\"verdicts\"] === undefined ||\n (Array.isArray(value[\"verdicts\"]) &&\n value[\"verdicts\"].every(isSessionVerdict))) &&\n (value[\"failed\"] === undefined ||\n (Array.isArray(value[\"failed\"]) && value[\"failed\"].every(isFailedCommand)));\n\nconst isProtocol = (value: unknown) =>\n value === undefined ||\n (typeof value === \"string\" && value !== \"*\" && protocolToken.test(value));\n\nconst isProducer = (value: unknown) => {\n if (value === undefined) return true;\n if (!isRecord(value) || !isRecord(value[\"wire\"])) return false;\n try {\n negotiationHeaders({\n wireVersion: value[\"wire\"] as StatewireProtocol.Range,\n applicationProtocols: value[\"protocols\"] as\n | readonly StatewireProtocol.Offer[]\n | \"*\",\n });\n return value[\"protocols\"] !== undefined;\n } catch {\n return false;\n }\n};\n\nconst isContextOperation = (value: unknown) => {\n if (!isRecord(value)) return false;\n if (\n value[\"doc\"] !== undefined &&\n (typeof value[\"doc\"] !== \"number\" ||\n !Number.isSafeInteger(value[\"doc\"]) ||\n value[\"doc\"] < 0)\n )\n return false;\n if (value[\"op\"] === \"mount\") {\n try {\n applyDocumentOps(\n { mode: \"mounted\", documents: new Map() },\n [value],\n undefined,\n true,\n );\n return true;\n } catch {\n return false;\n }\n }\n if (value[\"op\"] === \"unmount\") return true;\n if (![\"add\", \"replace\", \"remove\"].includes(value[\"op\"] as string))\n return false;\n return (\n Array.isArray(value[\"path\"]) &&\n value[\"path\"].every(\n (part) =>\n typeof part === \"string\" ||\n (typeof part === \"number\" && Number.isSafeInteger(part) && part >= 0),\n ) &&\n (value[\"op\"] === \"remove\" || \"value\" in value)\n );\n};\n\nconst isContext = (value: unknown) => {\n if (value === undefined) return true;\n if (\n !isRecord(value) ||\n !Array.isArray(value[\"ops\"]) ||\n !Array.isArray(value[\"pending\"])\n )\n return false;\n if (value[\"recover\"] !== undefined && value[\"recover\"] !== \"version-1\")\n return false;\n try {\n const documents = applyDocumentOps(\n { mode: \"mounted\", documents: new Map() },\n value[\"ops\"],\n undefined,\n true,\n );\n for (const entry of value[\"pending\"]) {\n if (\n !isRecord(entry) ||\n !isPositiveSafeInteger(entry[\"seq\"]) ||\n !Array.isArray(entry[\"ops\"])\n )\n return false;\n if (!entry[\"ops\"].every(isContextOperation)) return false;\n }\n return documents.mode === \"mounted\";\n } catch {\n return false;\n }\n};\n\nconst decode = (\n raw: string | null,\n legacyProtocol: KeyValueSessionStorage.Options[\"legacyProtocol\"],\n): StatewireClient.SessionRecord | null => {\n if (raw === null) return null;\n const parsed: unknown = JSON.parse(raw);\n if (!isSessionRecord(parsed))\n throw new Error(\"statewire: invalid stored session record\");\n const {\n statewireStorageVersion: format,\n savedAt: _savedAt,\n ...value\n } = parsed as StatewireClient.SessionRecord & {\n statewireStorageVersion?: unknown;\n savedAt?: unknown;\n };\n if (!isSessionRecord(value))\n throw new Error(\"statewire: invalid stored session record\");\n if (format !== undefined && format !== 1)\n throw new Error(\"statewire: unsupported session storage version\");\n if (legacyProtocol === undefined || format !== undefined) return value;\n const produced = {\n wire: { version: \"2026-09-07\" },\n protocols: [legacyProtocol],\n };\n return {\n ...value,\n ...(value.context === undefined && value.ctxNextSeq > 1\n ? {\n context: {\n ops: [\n {\n op: \"mount\",\n protocol: legacyProtocol.name,\n main: true,\n value: {},\n },\n ],\n pending: [],\n recover: \"version-1\",\n },\n }\n : {}),\n commands: value.commands.map((command) =>\n command.produced === undefined\n ? {\n ...command,\n protocol: command.protocol ?? legacyProtocol.name,\n produced,\n }\n : command,\n ),\n ...(value.failed === undefined\n ? {}\n : {\n failed: value.failed.map((entry) => ({\n ...entry,\n protocol: entry.protocol ?? legacyProtocol.name,\n produced: entry.produced ?? produced,\n })),\n }),\n };\n};\n\nconst active = new WeakMap<\n KeyValueSessionStorage.Backend,\n Map<string, number>\n>();\n\nconst sweep = async (\n options: KeyValueSessionStorage.Options,\n currentKey: string,\n quota = false,\n) => {\n const { backend, retention, namespace = \"\" } = options;\n if (!retention || !backend.getAllKeys) return;\n const entries: { key: string; savedAt: number }[] = [];\n const candidate = (raw: string | null) => {\n if (raw === null) return undefined;\n let value: unknown;\n try {\n value = JSON.parse(raw);\n } catch {\n return undefined;\n }\n const format = isRecord(value)\n ? value[\"statewireStorageVersion\"]\n : undefined;\n if (format !== undefined && format !== 1) return undefined;\n if (\n !isSessionRecord(value) ||\n value.commands.length ||\n value.failed?.length ||\n value.context?.pending.length\n )\n return undefined;\n const savedAt =\n (value as Record<string, unknown>)[\"savedAt\"] ??\n (value as Record<string, unknown>)[\"updatedAt\"];\n return typeof savedAt === \"number\" && Number.isFinite(savedAt)\n ? savedAt\n : undefined;\n };\n for (const key of await backend.getAllKeys()) {\n if (\n !key.startsWith(namespace) ||\n key === currentKey ||\n active.get(backend)?.has(key)\n )\n continue;\n const savedAt = candidate(await backend.getItem(key));\n if (savedAt !== undefined) entries.push({ key, savedAt });\n }\n entries.sort((a, b) => b.savedAt - a.savedAt);\n const now = Date.now();\n let retained = 1;\n const quotaStart = Math.floor(entries.length / 2);\n for (const [index, entry] of entries.entries()) {\n const expired =\n retention.maxAgeMs !== undefined &&\n now - entry.savedAt > retention.maxAgeMs;\n const overflow =\n retention.maxEntries !== undefined && retained >= retention.maxEntries;\n if (expired || overflow || (quota && index >= quotaStart)) {\n const remove = async () => {\n if (active.get(backend)?.has(entry.key)) return;\n if (candidate(await backend.getItem(entry.key)) !== entry.savedAt)\n return;\n if (active.get(backend)?.has(entry.key)) return;\n await backend.removeItem(entry.key);\n };\n if (backend.withUnused) await backend.withUnused(entry.key, remove);\n else await remove();\n } else retained++;\n }\n};\n\nconst isQuotaError = (error: unknown) =>\n error instanceof Error && error.name === \"QuotaExceededError\";\n\nconst useKeyValueSessionStorage = (\n options: KeyValueSessionStorage.Options,\n): StatewireClient.SessionStorage => {\n const {\n backend,\n key,\n namespace = \"\",\n legacyProtocol: suppliedLegacyProtocol,\n migrate,\n retention: suppliedRetention,\n onError,\n } = options;\n const latestErrorHandler = useRef(onError);\n latestErrorHandler.current = onError;\n const reportError = useMemo(\n () => (error: unknown) => latestErrorHandler.current?.(error),\n [],\n );\n const legacyName = suppliedLegacyProtocol?.name;\n const legacyVersion = suppliedLegacyProtocol?.version;\n const legacyProtocol = useMemo(\n () =>\n legacyName === undefined\n ? undefined\n : { name: legacyName, version: legacyVersion! },\n [legacyName, legacyVersion],\n );\n const retain = suppliedRetention !== undefined;\n const maxAgeMs = suppliedRetention?.maxAgeMs;\n const maxEntries = suppliedRetention?.maxEntries;\n const retention = useMemo(\n () =>\n retain\n ? {\n ...(maxAgeMs === undefined ? {} : { maxAgeMs }),\n ...(maxEntries === undefined ? {} : { maxEntries }),\n }\n : undefined,\n [retain, maxAgeMs, maxEntries],\n );\n const configured = useMemo(\n () => ({\n backend,\n key,\n namespace,\n ...(retention === undefined ? {} : { retention }),\n }),\n [backend, key, namespace, retention],\n );\n const storageKey = namespace + key;\n if (legacyProtocol !== undefined)\n negotiationHeaders({ applicationProtocols: [legacyProtocol] });\n if (retention !== undefined) {\n if (!namespace)\n throw new Error(\"statewire: session retention requires a namespace\");\n if (!backend.getAllKeys)\n throw new Error(\"statewire: session retention requires getAllKeys\");\n if (\n retention.maxAgeMs !== undefined &&\n (!Number.isFinite(retention.maxAgeMs) || retention.maxAgeMs < 0)\n )\n throw new Error(\"statewire: invalid session retention maxAgeMs\");\n if (\n retention.maxEntries !== undefined &&\n (!Number.isSafeInteger(retention.maxEntries) || retention.maxEntries < 1)\n )\n throw new Error(\"statewire: invalid session retention maxEntries\");\n }\n const state = useMemo(() => {\n let raw: string | null | Promise<string | null>;\n try {\n raw = backend.getItem(storageKey);\n } catch (cause) {\n const error = wrapStorageError(cause);\n reportError(error);\n throw error;\n }\n const state = {\n record: null as StatewireClient.SessionRecord | null,\n ready: undefined as Promise<void> | undefined,\n queue: Promise.resolve(),\n pending: 0,\n failure: undefined as unknown,\n asynchronous: typeof raw !== \"string\" && raw !== null,\n hydrated: typeof raw === \"string\" || raw === null,\n };\n if (state.asynchronous) {\n state.ready = Promise.resolve(raw)\n .then((value) => {\n state.record = decode(value, legacyProtocol);\n state.hydrated = true;\n })\n .catch((cause: unknown) => {\n const error = wrapStorageError(cause);\n if (state.failure !== error) reportError(error);\n state.failure = error;\n throw error;\n });\n state.ready.catch(() => {});\n } else {\n try {\n state.record = decode(raw as string | null, legacyProtocol);\n } catch (cause) {\n const error = wrapStorageError(cause);\n reportError(error);\n throw error;\n }\n }\n return state;\n }, [backend, storageKey, legacyProtocol, reportError]);\n\n useEffect(() => {\n const entries = active.get(backend) ?? new Map<string, number>();\n active.set(backend, entries);\n entries.set(storageKey, (entries.get(storageKey) ?? 0) + 1);\n return () => {\n const count = entries.get(storageKey)! - 1;\n if (count === 0) entries.delete(storageKey);\n else entries.set(storageKey, count);\n };\n }, [backend, storageKey]);\n\n useEffect(() => {\n if (retention !== undefined)\n void sweep(configured, storageKey).catch((cause: unknown) => {\n const error = wrapStorageError(cause);\n state.failure = error;\n reportError(error);\n });\n }, [state, configured, storageKey, retention, reportError]);\n\n return useMemo(\n () => ({\n ...(state.ready === undefined ? {} : { ready: state.ready }),\n load: () => {\n if (state.failure !== undefined) throw state.failure;\n if (!state.hydrated)\n throw new Error(\n \"statewire: await session storage readiness before loading\",\n );\n if (state.asynchronous || state.pending > 0) return state.record;\n let raw: string | null | Promise<string | null>;\n try {\n raw = backend.getItem(storageKey);\n } catch (cause) {\n const error = wrapStorageError(cause);\n reportError(error);\n throw error;\n }\n if (typeof raw !== \"string\" && raw !== null)\n throw new Error(\n \"statewire: storage backend changed from synchronous to asynchronous\",\n );\n try {\n return decode(raw, legacyProtocol);\n } catch (cause) {\n const error = wrapStorageError(cause);\n reportError(error);\n throw error;\n }\n },\n save: (record: StatewireClient.SessionRecord) => {\n if (state.failure !== undefined) throw state.failure;\n if (!state.hydrated)\n throw new Error(\n \"statewire: await session storage readiness before saving\",\n );\n if (!isSessionRecord(record))\n throw new Error(\"statewire: invalid session record\");\n const encoded = JSON.stringify({\n ...record,\n statewireStorageVersion: 1,\n savedAt: Date.now(),\n });\n state.record = record;\n const recoverQuota = async (error: unknown) => {\n if (!isQuotaError(error) || retention === undefined) throw error;\n await sweep(configured, storageKey, true);\n await backend.setItem(storageKey, encoded);\n };\n const write = async () => {\n try {\n await backend.setItem(storageKey, encoded);\n } catch (error) {\n await recoverQuota(error);\n }\n };\n if (!state.asynchronous && state.pending === 0) {\n let result: void | Promise<void>;\n try {\n result = backend.setItem(storageKey, encoded);\n } catch (error) {\n if (!isQuotaError(error) || retention === undefined) {\n state.failure = wrapStorageError(error);\n reportError(state.failure);\n throw state.failure;\n }\n result = recoverQuota(error);\n }\n if (result === undefined) return;\n state.pending++;\n state.queue = result;\n } else {\n state.pending++;\n state.queue = state.queue.then(() => state.ready).then(write);\n }\n state.queue = state.queue\n .catch((cause: unknown) => {\n const error = wrapStorageError(cause);\n if (state.failure !== error) reportError(error);\n state.failure = error;\n throw error;\n })\n .finally(() => {\n state.pending--;\n });\n state.queue.catch(() => {});\n },\n flush: () => {\n if (state.failure !== undefined) throw state.failure;\n if (!state.asynchronous && state.pending === 0) return;\n return (async () => {\n await state.ready;\n for (;;) {\n const queue = state.queue;\n await queue;\n if (queue === state.queue) return;\n }\n })();\n },\n ...(migrate === undefined ? {} : { migrate }),\n }),\n [\n state,\n backend,\n storageKey,\n legacyProtocol,\n migrate,\n retention,\n configured,\n reportError,\n ],\n );\n};\n\n/** Persist sessions through a synchronous or asynchronous string storage backend. */\nexport const KeyValueSessionStorage = resource(useKeyValueSessionStorage);\n\nexport namespace KeyValueSessionStorage {\n export type Backend = {\n getItem: (key: string) => string | null | Promise<string | null>;\n setItem: (key: string, value: string) => void | Promise<void>;\n removeItem: (key: string) => void | Promise<void>;\n getAllKeys?: () => readonly string[] | Promise<readonly string[]>;\n /** Runs cleanup under exclusive access, skipping keys in use by another runtime. */\n withUnused?: (key: string, action: () => Promise<void>) => Promise<void>;\n };\n export type Options = {\n backend: Backend;\n key: string;\n namespace?: string;\n /** The application format used by sessions saved before version negotiation. */\n legacyProtocol?: { name: string; version: string };\n migrate?: StatewireClient.SessionStorage[\"migrate\"];\n retention?: { maxAgeMs?: number; maxEntries?: number };\n onError?: (error: unknown) => void;\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"storage.js","names":["useEffect","useMemo","useRef","wrapStorageError","resource","StatewireClient","StatewireProtocol","negotiationHeaders","protocolToken","isRecord","value","Record","Array","isArray","isOptionalString","undefined","isPositiveSafeInteger","Number","isSafeInteger","isSessionCommand","SessionCommand","isProtocol","isProducer","isSessionVerdict","SessionVerdict","isSendFailure","SendFailure","isFailedCommand","FailedCommand","isFinite","isSessionRecord","SessionRecord","length","every","test","wireVersion","Range","applicationProtocols","Offer","decode","raw","legacyProtocol","KeyValueSessionStorage","Options","parsed","JSON","parse","Error","statewireStorageVersion","format","savedAt","_savedAt","ctxNextSeq","_ctxNextSeq","context","_context","produced","wire","version","protocols","commands","map","command","protocol","name","failed","entry","active","WeakMap","Backend","Map","sweep","options","currentKey","quota","backend","retention","namespace","getAllKeys","entries","key","candidate","startsWith","get","has","getItem","push","sort","a","b","now","Date","retained","quotaStart","Math","floor","index","expired","maxAgeMs","overflow","maxEntries","remove","removeItem","withUnused","isQuotaError","error","useKeyValueSessionStorage","SessionStorage","suppliedLegacyProtocol","migrate","suppliedRetention","onError","latestErrorHandler","current","reportError","legacyName","legacyVersion","retain","configured","storageKey","state","Promise","cause","record","ready","queue","resolve","pending","failure","asynchronous","hydrated","then","catch","set","count","delete","load","save","encoded","stringify","recoverQuota","setItem","write","result","finally","flush","action"],"sources":["../src/storage.ts"],"sourcesContent":["import { useEffect, useMemo, useRef } from \"react\";\nimport { wrapStorageError } from \"./client/storageError\";\nimport { resource } from \"@assistant-ui/tap\";\nimport type { StatewireClient } from \"./statewire\";\nimport type { StatewireProtocol } from \"./wire/protocol\";\nimport { negotiationHeaders, protocolToken } from \"./wire/protocol\";\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isOptionalString = (value: unknown): boolean =>\n value === undefined || typeof value === \"string\";\n\nconst isPositiveSafeInteger = (value: unknown): value is number =>\n typeof value === \"number\" && Number.isSafeInteger(value) && value > 0;\n\nconst isSessionCommand = (\n value: unknown,\n): value is StatewireClient.SessionCommand =>\n isRecord(value) &&\n isPositiveSafeInteger(value[\"seq\"]) &&\n isProtocol(value[\"protocol\"]) &&\n isProducer(value[\"produced\"]) &&\n typeof value[\"method\"] === \"string\" &&\n Array.isArray(value[\"params\"]);\n\nconst isSessionVerdict = (\n value: unknown,\n): value is StatewireClient.SessionVerdict =>\n isRecord(value) &&\n isPositiveSafeInteger(value[\"seq\"]) &&\n (value[\"type\"] === \"result\" ||\n value[\"type\"] === \"rejected\" ||\n value[\"type\"] === \"failed\" ||\n value[\"type\"] === \"crashed\" ||\n value[\"type\"] === \"expired\") &&\n isOptionalString(value[\"code\"]) &&\n isOptionalString(value[\"message\"]);\n\nconst isSendFailure = (\n value: unknown,\n): value is StatewireClient.SendFailure => {\n if (!isRecord(value)) return false;\n switch (value[\"fate\"]) {\n case \"rejected\":\n return (\n value[\"cause\"] === \"server\" &&\n isOptionalString(value[\"code\"]) &&\n isOptionalString(value[\"message\"])\n );\n case \"failed\":\n return value[\"cause\"] === \"server\" && isOptionalString(value[\"message\"]);\n case \"crashed\":\n case \"expired\":\n return value[\"cause\"] === \"server\";\n case \"aborted\":\n return (\n (value[\"cause\"] === \"unmount\" ||\n value[\"cause\"] === \"stream-gone\" ||\n value[\"cause\"] === \"session-lost\") &&\n (value[\"predecessorLost\"] === undefined ||\n value[\"predecessorLost\"] === true)\n );\n case \"lost\":\n return (\n value[\"cause\"] === \"unmount\" ||\n value[\"cause\"] === \"stream-gone\" ||\n value[\"cause\"] === \"session-lost\"\n );\n default:\n return false;\n }\n};\n\nconst isFailedCommand = (\n value: unknown,\n): value is StatewireClient.FailedCommand =>\n isRecord(value) &&\n isProtocol(value[\"protocol\"]) &&\n isProducer(value[\"produced\"]) &&\n typeof value[\"command\"] === \"string\" &&\n Array.isArray(value[\"params\"]) &&\n isSendFailure(value[\"failure\"]) &&\n typeof value[\"ts\"] === \"number\" &&\n Number.isFinite(value[\"ts\"]) &&\n isPositiveSafeInteger(value[\"seq\"]);\n\nconst isSessionRecord = (\n value: unknown,\n): value is StatewireClient.SessionRecord =>\n isRecord(value) &&\n typeof value[\"clientId\"] === \"string\" &&\n value[\"clientId\"].length > 0 &&\n isPositiveSafeInteger(value[\"nextSeq\"]) &&\n Array.isArray(value[\"commands\"]) &&\n value[\"commands\"].every(isSessionCommand) &&\n (value[\"verdicts\"] === undefined ||\n (Array.isArray(value[\"verdicts\"]) &&\n value[\"verdicts\"].every(isSessionVerdict))) &&\n (value[\"failed\"] === undefined ||\n (Array.isArray(value[\"failed\"]) && value[\"failed\"].every(isFailedCommand)));\n\nconst isProtocol = (value: unknown) =>\n value === undefined ||\n (typeof value === \"string\" && value !== \"*\" && protocolToken.test(value));\n\nconst isProducer = (value: unknown) => {\n if (value === undefined) return true;\n if (!isRecord(value) || !isRecord(value[\"wire\"])) return false;\n try {\n negotiationHeaders({\n wireVersion: value[\"wire\"] as StatewireProtocol.Range,\n applicationProtocols: value[\"protocols\"] as\n | readonly StatewireProtocol.Offer[]\n | \"*\",\n });\n return value[\"protocols\"] !== undefined;\n } catch {\n return false;\n }\n};\n\nconst decode = (\n raw: string | null,\n legacyProtocol: KeyValueSessionStorage.Options[\"legacyProtocol\"],\n): StatewireClient.SessionRecord | null => {\n if (raw === null) return null;\n const parsed: unknown = JSON.parse(raw);\n if (!isSessionRecord(parsed))\n throw new Error(\"statewire: invalid stored session record\");\n const {\n statewireStorageVersion: format,\n savedAt: _savedAt,\n ctxNextSeq: _ctxNextSeq,\n context: _context,\n ...value\n } = parsed as StatewireClient.SessionRecord & {\n statewireStorageVersion?: unknown;\n savedAt?: unknown;\n ctxNextSeq?: unknown;\n context?: unknown;\n };\n if (!isSessionRecord(value))\n throw new Error(\"statewire: invalid stored session record\");\n if (format !== undefined && format !== 1 && format !== 2)\n throw new Error(\"statewire: unsupported session storage version\");\n if (legacyProtocol === undefined || format !== undefined) return value;\n const produced = {\n wire: { version: \"2026-09-07\" },\n protocols: [legacyProtocol],\n };\n return {\n ...value,\n commands: value.commands.map((command) =>\n command.produced === undefined\n ? {\n ...command,\n protocol: command.protocol ?? legacyProtocol.name,\n produced,\n }\n : command,\n ),\n ...(value.failed === undefined\n ? {}\n : {\n failed: value.failed.map((entry) => ({\n ...entry,\n protocol: entry.protocol ?? legacyProtocol.name,\n produced: entry.produced ?? produced,\n })),\n }),\n };\n};\n\nconst active = new WeakMap<\n KeyValueSessionStorage.Backend,\n Map<string, number>\n>();\n\nconst sweep = async (\n options: KeyValueSessionStorage.Options,\n currentKey: string,\n quota = false,\n) => {\n const { backend, retention, namespace = \"\" } = options;\n if (!retention || !backend.getAllKeys) return;\n const entries: { key: string; savedAt: number }[] = [];\n const candidate = (raw: string | null) => {\n if (raw === null) return undefined;\n let value: unknown;\n try {\n value = JSON.parse(raw);\n } catch {\n return undefined;\n }\n const format = isRecord(value)\n ? value[\"statewireStorageVersion\"]\n : undefined;\n if (format !== undefined && format !== 1 && format !== 2) return undefined;\n if (\n !isSessionRecord(value) ||\n value.commands.length ||\n value.failed?.length\n )\n return undefined;\n const savedAt =\n (value as Record<string, unknown>)[\"savedAt\"] ??\n (value as Record<string, unknown>)[\"updatedAt\"];\n return typeof savedAt === \"number\" && Number.isFinite(savedAt)\n ? savedAt\n : undefined;\n };\n for (const key of await backend.getAllKeys()) {\n if (\n !key.startsWith(namespace) ||\n key === currentKey ||\n active.get(backend)?.has(key)\n )\n continue;\n const savedAt = candidate(await backend.getItem(key));\n if (savedAt !== undefined) entries.push({ key, savedAt });\n }\n entries.sort((a, b) => b.savedAt - a.savedAt);\n const now = Date.now();\n let retained = 1;\n const quotaStart = Math.floor(entries.length / 2);\n for (const [index, entry] of entries.entries()) {\n const expired =\n retention.maxAgeMs !== undefined &&\n now - entry.savedAt > retention.maxAgeMs;\n const overflow =\n retention.maxEntries !== undefined && retained >= retention.maxEntries;\n if (expired || overflow || (quota && index >= quotaStart)) {\n const remove = async () => {\n if (active.get(backend)?.has(entry.key)) return;\n if (candidate(await backend.getItem(entry.key)) !== entry.savedAt)\n return;\n if (active.get(backend)?.has(entry.key)) return;\n await backend.removeItem(entry.key);\n };\n if (backend.withUnused) await backend.withUnused(entry.key, remove);\n else await remove();\n } else retained++;\n }\n};\n\nconst isQuotaError = (error: unknown) =>\n error instanceof Error && error.name === \"QuotaExceededError\";\n\nconst useKeyValueSessionStorage = (\n options: KeyValueSessionStorage.Options,\n): StatewireClient.SessionStorage => {\n const {\n backend,\n key,\n namespace = \"\",\n legacyProtocol: suppliedLegacyProtocol,\n migrate,\n retention: suppliedRetention,\n onError,\n } = options;\n const latestErrorHandler = useRef(onError);\n latestErrorHandler.current = onError;\n const reportError = useMemo(\n () => (error: unknown) => latestErrorHandler.current?.(error),\n [],\n );\n const legacyName = suppliedLegacyProtocol?.name;\n const legacyVersion = suppliedLegacyProtocol?.version;\n const legacyProtocol = useMemo(\n () =>\n legacyName === undefined\n ? undefined\n : { name: legacyName, version: legacyVersion! },\n [legacyName, legacyVersion],\n );\n const retain = suppliedRetention !== undefined;\n const maxAgeMs = suppliedRetention?.maxAgeMs;\n const maxEntries = suppliedRetention?.maxEntries;\n const retention = useMemo(\n () =>\n retain\n ? {\n ...(maxAgeMs === undefined ? {} : { maxAgeMs }),\n ...(maxEntries === undefined ? {} : { maxEntries }),\n }\n : undefined,\n [retain, maxAgeMs, maxEntries],\n );\n const configured = useMemo(\n () => ({\n backend,\n key,\n namespace,\n ...(retention === undefined ? {} : { retention }),\n }),\n [backend, key, namespace, retention],\n );\n const storageKey = namespace + key;\n if (legacyProtocol !== undefined)\n negotiationHeaders({ applicationProtocols: [legacyProtocol] });\n if (retention !== undefined) {\n if (!namespace)\n throw new Error(\"statewire: session retention requires a namespace\");\n if (!backend.getAllKeys)\n throw new Error(\"statewire: session retention requires getAllKeys\");\n if (\n retention.maxAgeMs !== undefined &&\n (!Number.isFinite(retention.maxAgeMs) || retention.maxAgeMs < 0)\n )\n throw new Error(\"statewire: invalid session retention maxAgeMs\");\n if (\n retention.maxEntries !== undefined &&\n (!Number.isSafeInteger(retention.maxEntries) || retention.maxEntries < 1)\n )\n throw new Error(\"statewire: invalid session retention maxEntries\");\n }\n const state = useMemo(() => {\n let raw: string | null | Promise<string | null>;\n try {\n raw = backend.getItem(storageKey);\n } catch (cause) {\n const error = wrapStorageError(cause);\n reportError(error);\n throw error;\n }\n const state = {\n record: null as StatewireClient.SessionRecord | null,\n ready: undefined as Promise<void> | undefined,\n queue: Promise.resolve(),\n pending: 0,\n failure: undefined as unknown,\n asynchronous: typeof raw !== \"string\" && raw !== null,\n hydrated: typeof raw === \"string\" || raw === null,\n };\n if (state.asynchronous) {\n state.ready = Promise.resolve(raw)\n .then((value) => {\n state.record = decode(value, legacyProtocol);\n state.hydrated = true;\n })\n .catch((cause: unknown) => {\n const error = wrapStorageError(cause);\n if (state.failure !== error) reportError(error);\n state.failure = error;\n throw error;\n });\n state.ready.catch(() => {});\n } else {\n try {\n state.record = decode(raw as string | null, legacyProtocol);\n } catch (cause) {\n const error = wrapStorageError(cause);\n reportError(error);\n throw error;\n }\n }\n return state;\n }, [backend, storageKey, legacyProtocol, reportError]);\n\n useEffect(() => {\n const entries = active.get(backend) ?? new Map<string, number>();\n active.set(backend, entries);\n entries.set(storageKey, (entries.get(storageKey) ?? 0) + 1);\n return () => {\n const count = entries.get(storageKey)! - 1;\n if (count === 0) entries.delete(storageKey);\n else entries.set(storageKey, count);\n };\n }, [backend, storageKey]);\n\n useEffect(() => {\n if (retention !== undefined)\n void sweep(configured, storageKey).catch((cause: unknown) => {\n const error = wrapStorageError(cause);\n state.failure = error;\n reportError(error);\n });\n }, [state, configured, storageKey, retention, reportError]);\n\n return useMemo(\n () => ({\n ...(state.ready === undefined ? {} : { ready: state.ready }),\n load: () => {\n if (state.failure !== undefined) throw state.failure;\n if (!state.hydrated)\n throw new Error(\n \"statewire: await session storage readiness before loading\",\n );\n if (state.asynchronous || state.pending > 0) return state.record;\n let raw: string | null | Promise<string | null>;\n try {\n raw = backend.getItem(storageKey);\n } catch (cause) {\n const error = wrapStorageError(cause);\n reportError(error);\n throw error;\n }\n if (typeof raw !== \"string\" && raw !== null)\n throw new Error(\n \"statewire: storage backend changed from synchronous to asynchronous\",\n );\n try {\n return decode(raw, legacyProtocol);\n } catch (cause) {\n const error = wrapStorageError(cause);\n reportError(error);\n throw error;\n }\n },\n save: (record: StatewireClient.SessionRecord) => {\n if (state.failure !== undefined) throw state.failure;\n if (!state.hydrated)\n throw new Error(\n \"statewire: await session storage readiness before saving\",\n );\n if (!isSessionRecord(record))\n throw new Error(\"statewire: invalid session record\");\n const encoded = JSON.stringify({\n ...record,\n statewireStorageVersion: 2,\n savedAt: Date.now(),\n });\n state.record = record;\n const recoverQuota = async (error: unknown) => {\n if (!isQuotaError(error) || retention === undefined) throw error;\n await sweep(configured, storageKey, true);\n await backend.setItem(storageKey, encoded);\n };\n const write = async () => {\n try {\n await backend.setItem(storageKey, encoded);\n } catch (error) {\n await recoverQuota(error);\n }\n };\n if (!state.asynchronous && state.pending === 0) {\n let result: void | Promise<void>;\n try {\n result = backend.setItem(storageKey, encoded);\n } catch (error) {\n if (!isQuotaError(error) || retention === undefined) {\n state.failure = wrapStorageError(error);\n reportError(state.failure);\n throw state.failure;\n }\n result = recoverQuota(error);\n }\n if (result === undefined) return;\n state.pending++;\n state.queue = result;\n } else {\n state.pending++;\n state.queue = state.queue.then(() => state.ready).then(write);\n }\n state.queue = state.queue\n .catch((cause: unknown) => {\n const error = wrapStorageError(cause);\n if (state.failure !== error) reportError(error);\n state.failure = error;\n throw error;\n })\n .finally(() => {\n state.pending--;\n });\n state.queue.catch(() => {});\n },\n flush: () => {\n if (state.failure !== undefined) throw state.failure;\n if (!state.asynchronous && state.pending === 0) return;\n return (async () => {\n await state.ready;\n for (;;) {\n const queue = state.queue;\n await queue;\n if (queue === state.queue) return;\n }\n })();\n },\n ...(migrate === undefined ? {} : { migrate }),\n }),\n [\n state,\n backend,\n storageKey,\n legacyProtocol,\n migrate,\n retention,\n configured,\n reportError,\n ],\n );\n};\n\n/** Persist sessions through a synchronous or asynchronous string storage backend. */\nexport const KeyValueSessionStorage = resource(useKeyValueSessionStorage);\n\nexport namespace KeyValueSessionStorage {\n export type Backend = {\n getItem: (key: string) => string | null | Promise<string | null>;\n setItem: (key: string, value: string) => void | Promise<void>;\n removeItem: (key: string) => void | Promise<void>;\n getAllKeys?: () => readonly string[] | Promise<readonly string[]>;\n /** Runs cleanup under exclusive access, skipping keys in use by another runtime. */\n withUnused?: (key: string, action: () => Promise<void>) => Promise<void>;\n };\n export type Options = {\n backend: Backend;\n key: string;\n namespace?: string;\n /** The application format used by sessions saved before version negotiation. */\n legacyProtocol?: { name: string; version: string };\n migrate?: StatewireClient.SessionStorage[\"migrate\"];\n retention?: { maxAgeMs?: number; maxEntries?: number };\n onError?: (error: unknown) => void;\n };\n}\n"],"mappings":"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"}
@@ -20,7 +20,7 @@ type StatewireFrame = {
20
20
  type StatewireRemoteProps = {
21
21
  beforeAttach?: () => void | Promise<void>;
22
22
  clientId: string;
23
- /** Lanes to ride the attach frame (no-lease supersession only); records the included seqs. */
23
+ /** Lanes to ride the attach frame: the context restatement, plus cmds on no-lease supersession; records the included seqs. */
24
24
  takeAttachLanes: () => StatewireFrame | null;
25
25
  onHello: (args: {
26
26
  ops: unknown[];
@@ -1 +1 @@
1
- {"version":3,"file":"sse.js","names":["flushTapSync","resource","ResourceElement","createSseParser","timing","settleFin","classify","handleEnvelope","resolveHeaders","useLink","validateRefusalSyn","AttemptOutcome","Conn","StatewireCmd","StatewireEnvelope","StatewireSyn","Statewire","StatewireClient","StatewireHost","useStatewireRemote","negotiationHeaders","parseNegotiation","StatewireFrame","ctx","ops","seq","cmd","method","params","meta","StatewireRemoteProps","beforeAttach","Promise","clientId","takeAttachLanes","onHello","args","syn","onOps","cmds","onCmds","onRefusal","onIdle","level","onDetach","onFinish","Fin","StatewireSubmitResult","type","ConnectionState","RemoteLink","storageFailed","error","Error","connection","Connection","parked","send","frame","LEASE_HEADER","baseUrl","options","HttpOptions","url","replace","withExtra","headers","Headers","extra","Record","merged","name","value","Object","entries","set","timedFetch","input","init","RequestInit","signal","AbortSignal","timeout","Response","fetch","globalThis","consume","c","reader","ReadableStreamDefaultReader","Uint8Array","heartbeat","AbortController","dead","decoder","TextDecoder","parse","timer","setTimeout","abort","heartbeatTimeoutMs","result","ReadableStreamReadResult","read","clearTimeout","done","data","decode","stream","env","JSON","attemptStream","persistence","aborted","lease","authStale","has","negotiation","lanes","body","undefined","stringify","headerTimer","res","accept","cache","any","ok","clone","json","catch","fin","httpFin","get","getReader","cancel","postFrame","lifetime","retryAfterMs","header","trim","test","retry","Number","isSafeInteger","reason","status","message","code","refusalSyn","sendFrame","headerRetry","useSseLink","attemptOnce","SyntaxError","startsWith","useStatewireHttp","StatewireHttp","Commands","TState","TCommands"],"sources":["../../src/transports/sse.ts"],"sourcesContent":["import { flushTapSync, resource } from \"@assistant-ui/tap\";\nimport type { ResourceElement } from \"@assistant-ui/tap\";\nimport { createSseParser } from \"./sse-parser\";\nimport { timing } from \"../client/timing\";\nimport {\n settleFin,\n classify,\n handleEnvelope,\n resolveHeaders,\n useLink,\n validateRefusalSyn,\n} from \"../client/connection\";\nimport type {\n AttemptOutcome,\n Conn,\n StatewireCmd,\n StatewireEnvelope,\n StatewireSyn,\n} from \"../client/connection\";\nimport type { Statewire } from \"../types\";\nimport type { StatewireClient } from \"../statewire\";\nimport type { StatewireHost } from \"../host\";\nimport { useStatewireRemote } from \"./remote\";\nimport { negotiationHeaders, parseNegotiation } from \"../wire/protocol\";\n\n/** A client frame's lanes: stamped statements, strictly consecutive per lane. */\nexport type StatewireFrame = {\n ctx?: { ops: unknown[]; seq: number }[];\n cmd?: { method: string; params: unknown[]; meta?: unknown; seq: number }[];\n};\n\nexport type StatewireRemoteProps = {\n beforeAttach?: () => void | Promise<void>;\n clientId: string;\n /** Lanes to ride the attach frame (no-lease supersession only); records the included seqs. */\n takeAttachLanes: () => StatewireFrame | null;\n onHello: (args: { ops: unknown[]; syn: StatewireSyn }) => void;\n onOps: (args: { ops: unknown[]; cmds: StatewireCmd[] }) => void;\n onCmds: (cmds: StatewireCmd[]) => void;\n onRefusal: (syn: StatewireSyn) => void;\n onIdle: (level: boolean) => void;\n onDetach: () => void;\n onFinish: (args: StatewireHost.Fin) => void;\n};\n\nexport type StatewireSubmitResult =\n | { type: \"ok\" }\n | { type: \"refusal\"; syn: StatewireSyn }\n | { type: \"stale-lease\" }\n | { type: \"no-lease\" };\n\nexport type ConnectionState = StatewireClient.ConnectionState;\n\n/** The live wire a remote session drives: connection status plus frame submission. */\nexport type RemoteLink = {\n storageFailed: (error: Error) => void;\n connection: StatewireClient.Connection;\n /** A deferred first attach still waiting; `reconnect()` releases it. */\n parked: boolean;\n send: (frame: StatewireFrame) => Promise<StatewireSubmitResult>;\n};\n\nconst LEASE_HEADER = \"Statewire-Lease\";\n\nconst baseUrl = (options: StatewireClient.HttpOptions) =>\n options.url.replace(/\\/$/, \"\");\n\nconst withExtra = (headers: Headers, extra: Record<string, string>) => {\n const merged = new Headers(headers);\n for (const [name, value] of Object.entries(extra)) merged.set(name, value);\n return merged;\n};\n\nconst timedFetch = async (\n options: StatewireClient.HttpOptions,\n input: string,\n init: RequestInit & { signal: AbortSignal },\n timeout: AbortSignal,\n): Promise<Response> => {\n try {\n return await (options.fetch ?? globalThis.fetch)(input, init);\n } catch {\n throw classify(timeout);\n }\n};\n\nconst consume = async (\n c: Conn,\n reader: ReadableStreamDefaultReader<Uint8Array>,\n heartbeat: AbortController,\n dead: () => boolean,\n): Promise<AttemptOutcome> => {\n const decoder = new TextDecoder();\n const parse = createSseParser();\n for (;;) {\n const timer = setTimeout(\n () => heartbeat.abort(),\n timing.heartbeatTimeoutMs,\n );\n let result: ReadableStreamReadResult<Uint8Array>;\n try {\n result = await reader.read();\n } catch {\n throw classify(heartbeat.signal);\n } finally {\n clearTimeout(timer);\n }\n if (result.done) return \"ended\";\n for (const data of parse(decoder.decode(result.value, { stream: true }))) {\n if (dead()) return \"ended\";\n const env = JSON.parse(data) as StatewireEnvelope;\n const done = flushTapSync(() => handleEnvelope(c, env));\n if (done) return done;\n }\n }\n};\n\nconst attemptStream = async (\n c: Conn,\n options: StatewireClient.HttpOptions,\n abort: AbortController,\n): Promise<AttemptOutcome> => {\n const persistence = c.ctx.beforeAttach?.();\n if (persistence) await persistence;\n if (abort.signal.aborted) return \"ended\";\n c.lease = null;\n const headers = await resolveHeaders(options.headers, c.authStale);\n for (const [name, value] of Object.entries(negotiationHeaders(options))) {\n if (!headers.has(name)) headers.set(name, value);\n }\n c.negotiation = parseNegotiation(headers);\n c.authStale = false;\n const lanes = flushTapSync(() => c.ctx.takeAttachLanes());\n const body = lanes === null ? undefined : JSON.stringify(lanes);\n const heartbeat = new AbortController();\n const headerTimer = setTimeout(\n () => heartbeat.abort(),\n timing.heartbeatTimeoutMs,\n );\n let res: Response;\n try {\n res = await timedFetch(\n options,\n `${baseUrl(options)}/stream`,\n {\n method: body === undefined ? \"GET\" : \"POST\",\n headers: withExtra(headers, {\n \"content-type\": \"application/json\",\n \"Statewire-Client-Id\": c.ctx.clientId,\n accept: \"text/event-stream\",\n }),\n ...(body === undefined ? {} : { body }),\n cache: \"no-store\",\n signal: AbortSignal.any([abort.signal, heartbeat.signal]),\n },\n heartbeat.signal,\n );\n } finally {\n clearTimeout(headerTimer);\n }\n if (!res.ok) {\n const body = await res\n .clone()\n .json()\n .catch(() => null);\n return (\n flushTapSync(() =>\n body?.fin === undefined\n ? settleFin(c, httpFin(res))\n : handleEnvelope(c, body),\n ) ?? \"stop\"\n );\n }\n if (!res.body)\n throw new Error(\"statewire: protocol error: stream response has no body\");\n c.lease = res.headers.get(LEASE_HEADER);\n const reader = res.body.getReader();\n try {\n return await consume(c, reader, heartbeat, () => abort.signal.aborted);\n } finally {\n reader.cancel().catch(() => {});\n }\n};\n\nconst postFrame = async (\n c: Conn,\n options: StatewireClient.HttpOptions,\n lease: string,\n frame: StatewireFrame,\n timeout: AbortSignal,\n): Promise<Response> =>\n timedFetch(\n options,\n `${baseUrl(options)}/frames`,\n {\n method: \"POST\",\n headers: withExtra(new Headers(), {\n \"content-type\": \"application/json\",\n [LEASE_HEADER]: lease,\n }),\n body: JSON.stringify(frame),\n signal: AbortSignal.any([\n c.lifetime?.signal ?? AbortSignal.abort(),\n timeout,\n ]),\n },\n timeout,\n );\n\n// Delta-seconds only; anything else reads as absent.\nconst retryAfterMs = (res: Response): number | null => {\n const header = res.headers.get(\"retry-after\")?.trim();\n if (header === undefined || !/^\\d+$/.test(header)) return null;\n const retry = Number(header) * 1000;\n return Number.isSafeInteger(retry) ? retry : null;\n};\n\nconst httpFin = (res: Response): StatewireHost.Fin => ({\n reason:\n res.status === 401\n ? \"unauthorized\"\n : res.status === 403\n ? \"forbidden\"\n : res.status === 429 || res.status === 503\n ? \"reconnect\"\n : res.status >= 400 && res.status < 500\n ? \"protocol-error\"\n : \"error\",\n status: res.status,\n message: `HTTP ${res.status}`,\n ...(res.status === 429 || res.status === 503 ? { code: \"overloaded\" } : {}),\n ...(retryAfterMs(res) !== null ? { retry: retryAfterMs(res)! } : {}),\n});\n\nconst refusalSyn = async (res: Response): Promise<StatewireSyn> => {\n const body = (await res.json().catch(() => null)) as {\n syn?: StatewireSyn;\n } | null;\n const syn = body?.syn;\n if (typeof syn !== \"object\" || syn === null) {\n throw new Error(`HTTP ${res.status}`);\n }\n return validateRefusalSyn(syn);\n};\n\nconst sendFrame = async (\n c: Conn,\n options: StatewireClient.HttpOptions,\n frame: StatewireFrame,\n): Promise<StatewireSubmitResult> => {\n const lease = c.lease;\n if (lease === null) return { type: \"no-lease\" };\n const timeout = AbortSignal.timeout(timing.heartbeatTimeoutMs);\n const res = await postFrame(c, options, lease, frame, timeout);\n if (res.ok) return { type: \"ok\" };\n if (res.status === 429 || res.status === 409) {\n // The syn body retry wins; Retry-After covers 429s minted below the host.\n const headerRetry = res.status === 429 ? retryAfterMs(res) : null;\n const syn: StatewireSyn = await refusalSyn(res).catch((error: unknown) => {\n if (headerRetry === null) throw error;\n return { retry: headerRetry };\n });\n if (syn.retry === undefined && headerRetry !== null)\n syn.retry = headerRetry;\n return { type: \"refusal\", syn };\n }\n if (res.status === 423) return { type: \"stale-lease\" };\n flushTapSync(() => settleFin(c, httpFin(res)));\n c.abort?.abort();\n throw new Error(`HTTP ${res.status}`);\n};\n\nconst useSseLink = (\n options: StatewireClient.HttpOptions,\n ctx: StatewireRemoteProps,\n): RemoteLink =>\n useLink({\n ...options,\n ctx,\n attemptOnce: (c, abort) => attemptStream(c, options, abort),\n send: async (c, frame) => {\n try {\n return await sendFrame(c, options, frame);\n } catch (error) {\n if (\n error instanceof SyntaxError ||\n (error instanceof Error &&\n error.message.startsWith(\"statewire: protocol error:\"))\n ) {\n flushTapSync(() =>\n settleFin(c, { reason: \"protocol-error\", message: error.message }),\n );\n c.abort?.abort();\n }\n throw error;\n }\n },\n });\n\nconst useStatewireHttp = (\n options: StatewireClient.HttpOptions,\n): Statewire<any, any> => useStatewireRemote(options, useSseLink);\n\nexport const StatewireHttp = resource(useStatewireHttp) as <\n TState = any,\n TCommands extends Statewire.Commands = Statewire.Commands,\n>(\n options: StatewireClient.HttpOptions,\n) => ResourceElement<Statewire<TState | undefined, TCommands>>;\n"],"mappings":"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"}
1
+ {"version":3,"file":"sse.js","names":["flushTapSync","resource","ResourceElement","createSseParser","timing","settleFin","classify","handleEnvelope","resolveHeaders","useLink","validateRefusalSyn","AttemptOutcome","Conn","StatewireCmd","StatewireEnvelope","StatewireSyn","Statewire","StatewireClient","StatewireHost","useStatewireRemote","negotiationHeaders","parseNegotiation","StatewireFrame","ctx","ops","seq","cmd","method","params","meta","StatewireRemoteProps","beforeAttach","Promise","clientId","takeAttachLanes","onHello","args","syn","onOps","cmds","onCmds","onRefusal","onIdle","level","onDetach","onFinish","Fin","StatewireSubmitResult","type","ConnectionState","RemoteLink","storageFailed","error","Error","connection","Connection","parked","send","frame","LEASE_HEADER","baseUrl","options","HttpOptions","url","replace","withExtra","headers","Headers","extra","Record","merged","name","value","Object","entries","set","timedFetch","input","init","RequestInit","signal","AbortSignal","timeout","Response","fetch","globalThis","consume","c","reader","ReadableStreamDefaultReader","Uint8Array","heartbeat","AbortController","dead","decoder","TextDecoder","parse","timer","setTimeout","abort","heartbeatTimeoutMs","result","ReadableStreamReadResult","read","clearTimeout","done","data","decode","stream","env","JSON","attemptStream","persistence","aborted","lease","authStale","has","negotiation","lanes","body","undefined","stringify","headerTimer","res","accept","cache","any","ok","clone","json","catch","fin","httpFin","get","getReader","cancel","postFrame","lifetime","retryAfterMs","header","trim","test","retry","Number","isSafeInteger","reason","status","message","code","refusalSyn","sendFrame","headerRetry","useSseLink","attemptOnce","SyntaxError","startsWith","useStatewireHttp","StatewireHttp","Commands","TState","TCommands"],"sources":["../../src/transports/sse.ts"],"sourcesContent":["import { flushTapSync, resource } from \"@assistant-ui/tap\";\nimport type { ResourceElement } from \"@assistant-ui/tap\";\nimport { createSseParser } from \"./sse-parser\";\nimport { timing } from \"../client/timing\";\nimport {\n settleFin,\n classify,\n handleEnvelope,\n resolveHeaders,\n useLink,\n validateRefusalSyn,\n} from \"../client/connection\";\nimport type {\n AttemptOutcome,\n Conn,\n StatewireCmd,\n StatewireEnvelope,\n StatewireSyn,\n} from \"../client/connection\";\nimport type { Statewire } from \"../types\";\nimport type { StatewireClient } from \"../statewire\";\nimport type { StatewireHost } from \"../host\";\nimport { useStatewireRemote } from \"./remote\";\nimport { negotiationHeaders, parseNegotiation } from \"../wire/protocol\";\n\n/** A client frame's lanes: stamped statements, strictly consecutive per lane. */\nexport type StatewireFrame = {\n ctx?: { ops: unknown[]; seq: number }[];\n cmd?: { method: string; params: unknown[]; meta?: unknown; seq: number }[];\n};\n\nexport type StatewireRemoteProps = {\n beforeAttach?: () => void | Promise<void>;\n clientId: string;\n /** Lanes to ride the attach frame: the context restatement, plus cmds on no-lease supersession; records the included seqs. */\n takeAttachLanes: () => StatewireFrame | null;\n onHello: (args: { ops: unknown[]; syn: StatewireSyn }) => void;\n onOps: (args: { ops: unknown[]; cmds: StatewireCmd[] }) => void;\n onCmds: (cmds: StatewireCmd[]) => void;\n onRefusal: (syn: StatewireSyn) => void;\n onIdle: (level: boolean) => void;\n onDetach: () => void;\n onFinish: (args: StatewireHost.Fin) => void;\n};\n\nexport type StatewireSubmitResult =\n | { type: \"ok\" }\n | { type: \"refusal\"; syn: StatewireSyn }\n | { type: \"stale-lease\" }\n | { type: \"no-lease\" };\n\nexport type ConnectionState = StatewireClient.ConnectionState;\n\n/** The live wire a remote session drives: connection status plus frame submission. */\nexport type RemoteLink = {\n storageFailed: (error: Error) => void;\n connection: StatewireClient.Connection;\n /** A deferred first attach still waiting; `reconnect()` releases it. */\n parked: boolean;\n send: (frame: StatewireFrame) => Promise<StatewireSubmitResult>;\n};\n\nconst LEASE_HEADER = \"Statewire-Lease\";\n\nconst baseUrl = (options: StatewireClient.HttpOptions) =>\n options.url.replace(/\\/$/, \"\");\n\nconst withExtra = (headers: Headers, extra: Record<string, string>) => {\n const merged = new Headers(headers);\n for (const [name, value] of Object.entries(extra)) merged.set(name, value);\n return merged;\n};\n\nconst timedFetch = async (\n options: StatewireClient.HttpOptions,\n input: string,\n init: RequestInit & { signal: AbortSignal },\n timeout: AbortSignal,\n): Promise<Response> => {\n try {\n return await (options.fetch ?? globalThis.fetch)(input, init);\n } catch {\n throw classify(timeout);\n }\n};\n\nconst consume = async (\n c: Conn,\n reader: ReadableStreamDefaultReader<Uint8Array>,\n heartbeat: AbortController,\n dead: () => boolean,\n): Promise<AttemptOutcome> => {\n const decoder = new TextDecoder();\n const parse = createSseParser();\n for (;;) {\n const timer = setTimeout(\n () => heartbeat.abort(),\n timing.heartbeatTimeoutMs,\n );\n let result: ReadableStreamReadResult<Uint8Array>;\n try {\n result = await reader.read();\n } catch {\n throw classify(heartbeat.signal);\n } finally {\n clearTimeout(timer);\n }\n if (result.done) return \"ended\";\n for (const data of parse(decoder.decode(result.value, { stream: true }))) {\n if (dead()) return \"ended\";\n const env = JSON.parse(data) as StatewireEnvelope;\n const done = flushTapSync(() => handleEnvelope(c, env));\n if (done) return done;\n }\n }\n};\n\nconst attemptStream = async (\n c: Conn,\n options: StatewireClient.HttpOptions,\n abort: AbortController,\n): Promise<AttemptOutcome> => {\n const persistence = c.ctx.beforeAttach?.();\n if (persistence) await persistence;\n if (abort.signal.aborted) return \"ended\";\n c.lease = null;\n const headers = await resolveHeaders(options.headers, c.authStale);\n for (const [name, value] of Object.entries(negotiationHeaders(options))) {\n if (!headers.has(name)) headers.set(name, value);\n }\n c.negotiation = parseNegotiation(headers);\n c.authStale = false;\n const lanes = flushTapSync(() => c.ctx.takeAttachLanes());\n const body = lanes === null ? undefined : JSON.stringify(lanes);\n const heartbeat = new AbortController();\n const headerTimer = setTimeout(\n () => heartbeat.abort(),\n timing.heartbeatTimeoutMs,\n );\n let res: Response;\n try {\n res = await timedFetch(\n options,\n `${baseUrl(options)}/stream`,\n {\n method: body === undefined ? \"GET\" : \"POST\",\n headers: withExtra(headers, {\n \"content-type\": \"application/json\",\n \"Statewire-Client-Id\": c.ctx.clientId,\n accept: \"text/event-stream\",\n }),\n ...(body === undefined ? {} : { body }),\n cache: \"no-store\",\n signal: AbortSignal.any([abort.signal, heartbeat.signal]),\n },\n heartbeat.signal,\n );\n } finally {\n clearTimeout(headerTimer);\n }\n if (!res.ok) {\n const body = await res\n .clone()\n .json()\n .catch(() => null);\n return (\n flushTapSync(() =>\n body?.fin === undefined\n ? settleFin(c, httpFin(res))\n : handleEnvelope(c, body),\n ) ?? \"stop\"\n );\n }\n if (!res.body)\n throw new Error(\"statewire: protocol error: stream response has no body\");\n c.lease = res.headers.get(LEASE_HEADER);\n const reader = res.body.getReader();\n try {\n return await consume(c, reader, heartbeat, () => abort.signal.aborted);\n } finally {\n reader.cancel().catch(() => {});\n }\n};\n\nconst postFrame = async (\n c: Conn,\n options: StatewireClient.HttpOptions,\n lease: string,\n frame: StatewireFrame,\n timeout: AbortSignal,\n): Promise<Response> =>\n timedFetch(\n options,\n `${baseUrl(options)}/frames`,\n {\n method: \"POST\",\n headers: withExtra(new Headers(), {\n \"content-type\": \"application/json\",\n [LEASE_HEADER]: lease,\n }),\n body: JSON.stringify(frame),\n signal: AbortSignal.any([\n c.lifetime?.signal ?? AbortSignal.abort(),\n timeout,\n ]),\n },\n timeout,\n );\n\n// Delta-seconds only; anything else reads as absent.\nconst retryAfterMs = (res: Response): number | null => {\n const header = res.headers.get(\"retry-after\")?.trim();\n if (header === undefined || !/^\\d+$/.test(header)) return null;\n const retry = Number(header) * 1000;\n return Number.isSafeInteger(retry) ? retry : null;\n};\n\nconst httpFin = (res: Response): StatewireHost.Fin => ({\n reason:\n res.status === 401\n ? \"unauthorized\"\n : res.status === 403\n ? \"forbidden\"\n : res.status === 429 || res.status === 503\n ? \"reconnect\"\n : res.status >= 400 && res.status < 500\n ? \"protocol-error\"\n : \"error\",\n status: res.status,\n message: `HTTP ${res.status}`,\n ...(res.status === 429 || res.status === 503 ? { code: \"overloaded\" } : {}),\n ...(retryAfterMs(res) !== null ? { retry: retryAfterMs(res)! } : {}),\n});\n\nconst refusalSyn = async (res: Response): Promise<StatewireSyn> => {\n const body = (await res.json().catch(() => null)) as {\n syn?: StatewireSyn;\n } | null;\n const syn = body?.syn;\n if (typeof syn !== \"object\" || syn === null) {\n throw new Error(`HTTP ${res.status}`);\n }\n return validateRefusalSyn(syn);\n};\n\nconst sendFrame = async (\n c: Conn,\n options: StatewireClient.HttpOptions,\n frame: StatewireFrame,\n): Promise<StatewireSubmitResult> => {\n const lease = c.lease;\n if (lease === null) return { type: \"no-lease\" };\n const timeout = AbortSignal.timeout(timing.heartbeatTimeoutMs);\n const res = await postFrame(c, options, lease, frame, timeout);\n if (res.ok) return { type: \"ok\" };\n if (res.status === 429 || res.status === 409) {\n // The syn body retry wins; Retry-After covers 429s minted below the host.\n const headerRetry = res.status === 429 ? retryAfterMs(res) : null;\n const syn: StatewireSyn = await refusalSyn(res).catch((error: unknown) => {\n if (headerRetry === null) throw error;\n return { retry: headerRetry };\n });\n if (syn.retry === undefined && headerRetry !== null)\n syn.retry = headerRetry;\n return { type: \"refusal\", syn };\n }\n if (res.status === 423) return { type: \"stale-lease\" };\n flushTapSync(() => settleFin(c, httpFin(res)));\n c.abort?.abort();\n throw new Error(`HTTP ${res.status}`);\n};\n\nconst useSseLink = (\n options: StatewireClient.HttpOptions,\n ctx: StatewireRemoteProps,\n): RemoteLink =>\n useLink({\n ...options,\n ctx,\n attemptOnce: (c, abort) => attemptStream(c, options, abort),\n send: async (c, frame) => {\n try {\n return await sendFrame(c, options, frame);\n } catch (error) {\n if (\n error instanceof SyntaxError ||\n (error instanceof Error &&\n error.message.startsWith(\"statewire: protocol error:\"))\n ) {\n flushTapSync(() =>\n settleFin(c, { reason: \"protocol-error\", message: error.message }),\n );\n c.abort?.abort();\n }\n throw error;\n }\n },\n });\n\nconst useStatewireHttp = (\n options: StatewireClient.HttpOptions,\n): Statewire<any, any> => useStatewireRemote(options, useSseLink);\n\nexport const StatewireHttp = resource(useStatewireHttp) as <\n TState = any,\n TCommands extends Statewire.Commands = Statewire.Commands,\n>(\n options: StatewireClient.HttpOptions,\n) => ResourceElement<Statewire<TState | undefined, TCommands>>;\n"],"mappings":"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"}
@@ -50,6 +50,7 @@ const createMockTransport = (initialState, protocol = "default") => {
50
50
  ctxSeq: 0
51
51
  });
52
52
  const rec = clients.get(id);
53
+ rec.ctxSeq = 0;
53
54
  const frame = JSON.parse(String(init?.body ?? "{}"));
54
55
  admit(id, frame);
55
56
  return openSSEStream(streams, id, (handle) => {
@@ -62,7 +63,7 @@ const createMockTransport = (initialState, protocol = "default") => {
62
63
  }],
63
64
  syn: {
64
65
  seq: rec.seq,
65
- ctxSeq: rec.ctxSeq,
66
+ ctxSeq: 0,
66
67
  version: "2026-09-07",
67
68
  protocols: [{
68
69
  name: protocol,
@@ -1 +1 @@
1
- {"version":3,"file":"testing.js","names":["ResourceElement","StatewireEnvelope","StatewireHttp","StatewireFrame","Statewire","openSSEStream","sseFrame","FakeStreamHandle","tick","Promise","r","setTimeout","applyOptimistic","MockTransport","transport","TState","commands","method","params","context","setState","state","applied","seq","settle","payload","sendEnvelope","envelope","finish","reason","message","clientId","attached","mockCount","createMockTransport","initialState","protocol","finished","clients","Map","ctxSeq","streams","Set","stream","send","admit","id","frame","rec","get","statement","ctx","Math","max","root","ops","value","push","cmd","fakeFetch","input","RequestInfo","URL","init","RequestInit","Response","TypeError","url","String","headers","Headers","endsWith","has","set","JSON","parse","body","handle","op","main","syn","version","protocols","name","signal","undefined","json","status","fetch","next","path","type","fin","close","size"],"sources":["../../src/transports/testing.ts"],"sourcesContent":["import type { ResourceElement } from \"@assistant-ui/tap\";\nimport type { StatewireEnvelope } from \"../client/connection\";\nimport { StatewireHttp } from \"./sse\";\nimport type { StatewireFrame } from \"./sse\";\nimport type { Statewire } from \"../types\";\nimport { openSSEStream, sseFrame } from \"./fake-sse\";\nimport type { FakeStreamHandle } from \"./fake-sse\";\n\nexport * from \"./fixtures\";\n\n/** Let queued microtasks and op batches drain through the statewire client. */\nexport const tick = () => new Promise<void>((r) => setTimeout(r, 0));\n\nexport { applyOptimistic } from \"../util/recordMutations\";\n\nexport type MockTransport<TState> = {\n /** Pass as the `transport:` option of useStatewire / Harness. */\n transport: ResourceElement<Statewire<TState | undefined, any>>;\n /** Captured statements in arrival order: cmd as `{method, params}`, ctx as `{context}`. */\n commands: { method?: string; params?: unknown[]; context?: unknown }[];\n /** Replace the replicated state and broadcast it as a root snapshot op. */\n setState: (state: TState) => Promise<void>;\n /** Deliver a bare ack cmd packet on the stream. */\n applied: (seq: number) => Promise<void>;\n /** Deliver a terminal result cmd packet on the stream. */\n settle: (seq: number, payload?: unknown) => Promise<void>;\n /** Deliver an arbitrary envelope on the stream. */\n sendEnvelope: (envelope: StatewireEnvelope) => Promise<void>;\n /** End the stream with a fin; further attaches fail like a dead server. */\n finish: (reason: string, message?: string) => Promise<void>;\n readonly clientId: string;\n readonly attached: boolean;\n};\n\n/**\n * A mock statewire backend: serves `initialState` as the attach snapshot,\n * records submitted frames (receipt only — acks and results are delivered\n * from test code), and exposes the wire as a transport ResourceElement.\n */\nlet mockCount = 0;\n\nexport const createMockTransport = <TState>(\n initialState: TState,\n protocol = \"default\",\n): MockTransport<TState> => {\n let state: unknown = initialState;\n let clientId = \"\";\n let finished = false;\n const clients = new Map<string, { seq: number; ctxSeq: number }>();\n const streams = new Set<FakeStreamHandle>();\n const commands: MockTransport<unknown>[\"commands\"] = [];\n\n const sendEnvelope = (envelope: StatewireEnvelope) => {\n for (const stream of streams) stream.send(sseFrame(envelope));\n return tick();\n };\n\n const admit = (id: string, frame: StatewireFrame) => {\n const rec = clients.get(id)!;\n for (const statement of frame.ctx ?? []) {\n if (statement.seq <= rec.ctxSeq) continue;\n rec.ctxSeq = Math.max(rec.ctxSeq, statement.seq);\n const root = statement.ops[0] as { value?: unknown } | undefined;\n commands.push({ context: root?.value });\n }\n for (const statement of frame.cmd ?? []) {\n if (statement.seq <= rec.seq) continue;\n rec.seq = Math.max(rec.seq, statement.seq);\n commands.push({ method: statement.method, params: statement.params });\n }\n };\n\n const fakeFetch = (async (\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> => {\n if (finished) throw new TypeError(\"fetch failed (finished)\");\n const url = String(input);\n const headers = new Headers(init?.headers);\n const id = headers.get(\"Statewire-Client-Id\") ?? \"\";\n if (url.endsWith(\"/stream\")) {\n clientId = id;\n if (!clients.has(id)) clients.set(id, { seq: 0, ctxSeq: 0 });\n const rec = clients.get(id)!;\n const frame = JSON.parse(String(init?.body ?? \"{}\")) as StatewireFrame;\n admit(id, frame);\n return openSSEStream(\n streams,\n id,\n (handle) => {\n handle.send(\n sseFrame({\n ops: [{ op: \"mount\", protocol, main: true, value: state }],\n syn: {\n seq: rec.seq,\n ctxSeq: rec.ctxSeq,\n version: \"2026-09-07\",\n protocols: [{ name: protocol, version: \"2026-09-07\" }],\n },\n }),\n );\n },\n init?.signal ?? undefined,\n { \"Statewire-Lease\": \"mock-lease\" },\n );\n }\n if (url.endsWith(\"/frames\")) {\n admit(clientId, JSON.parse(String(init?.body ?? \"{}\")) as StatewireFrame);\n return Response.json({});\n }\n return new Response(null, { status: 404 });\n }) as typeof fetch;\n\n return {\n transport: StatewireHttp({\n url: `http://mock-${++mockCount}/api`,\n fetch: fakeFetch,\n }),\n commands,\n setState: (next) => {\n state = next;\n return sendEnvelope({ ops: [{ op: \"replace\", path: [], value: next }] });\n },\n applied: (seq) => sendEnvelope({ cmd: [{ seq, type: \"applied\" }] }),\n settle: (seq, payload) =>\n sendEnvelope({\n cmd: [\n {\n seq,\n type: \"result\",\n ...(payload !== undefined && { payload }),\n },\n ],\n }),\n sendEnvelope,\n finish: async (reason, message) => {\n await sendEnvelope({\n fin: { reason, ...(message !== undefined && { message }) },\n });\n finished = true;\n for (const stream of [...streams]) stream.close();\n await tick();\n },\n get clientId() {\n return clientId;\n },\n get attached() {\n return streams.size > 0;\n },\n };\n};\n"],"mappings":";;;;;;AAWA,MAAaQ,aAAa,IAAIC,SAAeC,MAAMC,WAAWD,GAAG,CAAC,CAAC;;;;;;AA4BnE,IAAIuB,YAAY;AAEhB,MAAaC,uBACXC,cACAC,WAAW,cACe;CAC1B,IAAIf,QAAiBc;CACrB,IAAIJ,WAAW;CACf,IAAIM,WAAW;CACf,MAAMC,0BAAU,IAAIC,IAA6C;CACjE,MAAME,0BAAU,IAAIC,IAAsB;CAC1C,MAAM1B,WAA+C,CAAA;CAErD,MAAMU,gBAAgBC,aAAgC;EACpD,KAAK,MAAMgB,UAAUF,SAASE,OAAOC,KAAKtC,SAASqB,QAAQ,CAAC;EAC5D,OAAOnB,KAAK;CACd;CAEA,MAAMqC,SAASC,IAAYC,UAA0B;EACnD,MAAMC,MAAMV,QAAQW,IAAIH,EAAE;EAC1B,KAAK,MAAMI,aAAaH,MAAMI,OAAO,CAAA,GAAI;GACvC,IAAID,UAAU3B,OAAOyB,IAAIR,QAAQ;GACjCQ,IAAIR,SAASY,KAAKC,IAAIL,IAAIR,QAAQU,UAAU3B,GAAG;GAC/C,MAAM+B,OAAOJ,UAAUK,IAAI;GAC3BvC,SAASyC,KAAK,EAAEtC,SAASmC,MAAME,MAAM,CAAC;EACxC;EACA,KAAK,MAAMN,aAAaH,MAAMW,OAAO,CAAA,GAAI;GACvC,IAAIR,UAAU3B,OAAOyB,IAAIzB,KAAK;GAC9ByB,IAAIzB,MAAM6B,KAAKC,IAAIL,IAAIzB,KAAK2B,UAAU3B,GAAG;GACzCP,SAASyC,KAAK;IAAExC,QAAQiC,UAAUjC;IAAQC,QAAQgC,UAAUhC;GAAO,CAAC;EACtE;CACF;CAEA,MAAMyC,aAAa,OACjBC,OACAG,SACsB;EACtB,IAAI1B,UAAU,MAAM,IAAI6B,UAAU,yBAAyB;EAC3D,MAAMC,MAAMC,OAAOR,KAAK;EAExB,MAAMd,KAAKuB,IADSC,QAAQP,MAAMM,OACvBA,CAAO,CAACpB,IAAI,qBAAqB,KAAK;EACjD,IAAIkB,IAAII,SAAS,SAAS,GAAG;GAC3BxC,WAAWe;GACX,IAAI,CAACR,QAAQkC,IAAI1B,EAAE,GAAGR,QAAQmC,IAAI3B,IAAI;IAAEvB,KAAK;IAAGiB,QAAQ;GAAE,CAAC;GAC3D,MAAMQ,MAAMV,QAAQW,IAAIH,EAAE;GAC1B,MAAMC,QAAQ2B,KAAKC,MAAMP,OAAOL,MAAMa,QAAQ,IAAI,CAAC;GACnD/B,MAAMC,IAAIC,KAAK;GACf,OAAO1C,cACLoC,SACAK,KACC+B,WAAW;IACVA,OAAOjC,KACLtC,SAAS;KACPiD,KAAK,CAAC;MAAEuB,IAAI;MAAS1C;MAAU2C,MAAM;MAAMvB,OAAOnC;KAAM,CAAC;KACzD2D,KAAK;MACHzD,KAAKyB,IAAIzB;MACTiB,QAAQQ,IAAIR;MACZyC,SAAS;MACTC,WAAW,CAAC;OAAEC,MAAM/C;OAAU6C,SAAS;MAAa,CAAC;KACvD;IACF,CAAC,CACH;GACF,GACAlB,MAAMqB,UAAUC,KAAAA,GAChB,EAAE,mBAAmB,aAAa,CACpC;EACF;EACA,IAAIlB,IAAII,SAAS,SAAS,GAAG;GAC3B1B,MAAMd,UAAU2C,KAAKC,MAAMP,OAAOL,MAAMa,QAAQ,IAAI,CAAC,CAAmB;GACxE,OAAOX,SAASqB,KAAK,CAAC,CAAC;EACzB;EACA,OAAO,IAAIrB,SAAS,MAAM,EAAEsB,QAAQ,IAAI,CAAC;CAC3C;CAEA,OAAO;EACLzE,WAAWZ,cAAc;GACvBiE,KAAK,eAAe,EAAElC,UAAS;GAC/BuD,OAAO7B;EACT,CAAC;EACD3C;EACAI,WAAWqE,SAAS;GAClBpE,QAAQoE;GACR,OAAO/D,aAAa,EAAE6B,KAAK,CAAC;IAAEuB,IAAI;IAAWY,MAAM,CAAA;IAAIlC,OAAOiC;GAAK,CAAC,EAAE,CAAC;EACzE;EACAnE,UAAUC,QAAQG,aAAa,EAAEgC,KAAK,CAAC;GAAEnC;GAAKoE,MAAM;EAAU,CAAC,EAAE,CAAC;EAClEnE,SAASD,KAAKE,YACZC,aAAa,EACXgC,KAAK,CACH;GACEnC;GACAoE,MAAM;GACN,GAAIlE,YAAY4D,KAAAA,KAAa,EAAE5D,QAAQ;EACzC,CAAC,EAEL,CAAC;EACHC;EACAE,QAAQ,OAAOC,QAAQC,YAAY;GACjC,MAAMJ,aAAa,EACjBkE,KAAK;IAAE/D;IAAQ,GAAIC,YAAYuD,KAAAA,KAAa,EAAEvD,QAAQ;GAAG,EAC3D,CAAC;GACDO,WAAW;GACX,KAAK,MAAMM,UAAU,CAAC,GAAGF,OAAO,GAAGE,OAAOkD,MAAM;GAChD,MAAMrF,KAAK;EACb;EACA,IAAIuB,WAAW;GACb,OAAOA;EACT;EACA,IAAIC,WAAW;GACb,OAAOS,QAAQqD,OAAO;EACxB;CACF;AACF"}
1
+ {"version":3,"file":"testing.js","names":["ResourceElement","StatewireEnvelope","StatewireHttp","StatewireFrame","Statewire","openSSEStream","sseFrame","FakeStreamHandle","tick","Promise","r","setTimeout","applyOptimistic","MockTransport","transport","TState","commands","method","params","context","setState","state","applied","seq","settle","payload","sendEnvelope","envelope","finish","reason","message","clientId","attached","mockCount","createMockTransport","initialState","protocol","finished","clients","Map","ctxSeq","streams","Set","stream","send","admit","id","frame","rec","get","statement","ctx","Math","max","root","ops","value","push","cmd","fakeFetch","input","RequestInfo","URL","init","RequestInit","Response","TypeError","url","String","headers","Headers","endsWith","has","set","JSON","parse","body","handle","op","main","syn","version","protocols","name","signal","undefined","json","status","fetch","next","path","type","fin","close","size"],"sources":["../../src/transports/testing.ts"],"sourcesContent":["import type { ResourceElement } from \"@assistant-ui/tap\";\nimport type { StatewireEnvelope } from \"../client/connection\";\nimport { StatewireHttp } from \"./sse\";\nimport type { StatewireFrame } from \"./sse\";\nimport type { Statewire } from \"../types\";\nimport { openSSEStream, sseFrame } from \"./fake-sse\";\nimport type { FakeStreamHandle } from \"./fake-sse\";\n\nexport * from \"./fixtures\";\n\n/** Let queued microtasks and op batches drain through the statewire client. */\nexport const tick = () => new Promise<void>((r) => setTimeout(r, 0));\n\nexport { applyOptimistic } from \"../util/recordMutations\";\n\nexport type MockTransport<TState> = {\n /** Pass as the `transport:` option of useStatewire / Harness. */\n transport: ResourceElement<Statewire<TState | undefined, any>>;\n /** Captured statements in arrival order: cmd as `{method, params}`, ctx as `{context}`. */\n commands: { method?: string; params?: unknown[]; context?: unknown }[];\n /** Replace the replicated state and broadcast it as a root snapshot op. */\n setState: (state: TState) => Promise<void>;\n /** Deliver a bare ack cmd packet on the stream. */\n applied: (seq: number) => Promise<void>;\n /** Deliver a terminal result cmd packet on the stream. */\n settle: (seq: number, payload?: unknown) => Promise<void>;\n /** Deliver an arbitrary envelope on the stream. */\n sendEnvelope: (envelope: StatewireEnvelope) => Promise<void>;\n /** End the stream with a fin; further attaches fail like a dead server. */\n finish: (reason: string, message?: string) => Promise<void>;\n readonly clientId: string;\n readonly attached: boolean;\n};\n\n/**\n * A mock statewire backend: serves `initialState` as the attach snapshot,\n * records submitted frames (receipt only — acks and results are delivered\n * from test code), and exposes the wire as a transport ResourceElement.\n */\nlet mockCount = 0;\n\nexport const createMockTransport = <TState>(\n initialState: TState,\n protocol = \"default\",\n): MockTransport<TState> => {\n let state: unknown = initialState;\n let clientId = \"\";\n let finished = false;\n const clients = new Map<string, { seq: number; ctxSeq: number }>();\n const streams = new Set<FakeStreamHandle>();\n const commands: MockTransport<unknown>[\"commands\"] = [];\n\n const sendEnvelope = (envelope: StatewireEnvelope) => {\n for (const stream of streams) stream.send(sseFrame(envelope));\n return tick();\n };\n\n const admit = (id: string, frame: StatewireFrame) => {\n const rec = clients.get(id)!;\n for (const statement of frame.ctx ?? []) {\n if (statement.seq <= rec.ctxSeq) continue;\n rec.ctxSeq = Math.max(rec.ctxSeq, statement.seq);\n const root = statement.ops[0] as { value?: unknown } | undefined;\n commands.push({ context: root?.value });\n }\n for (const statement of frame.cmd ?? []) {\n if (statement.seq <= rec.seq) continue;\n rec.seq = Math.max(rec.seq, statement.seq);\n commands.push({ method: statement.method, params: statement.params });\n }\n };\n\n const fakeFetch = (async (\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> => {\n if (finished) throw new TypeError(\"fetch failed (finished)\");\n const url = String(input);\n const headers = new Headers(init?.headers);\n const id = headers.get(\"Statewire-Client-Id\") ?? \"\";\n if (url.endsWith(\"/stream\")) {\n clientId = id;\n if (!clients.has(id)) clients.set(id, { seq: 0, ctxSeq: 0 });\n const rec = clients.get(id)!;\n rec.ctxSeq = 0;\n const frame = JSON.parse(String(init?.body ?? \"{}\")) as StatewireFrame;\n admit(id, frame);\n return openSSEStream(\n streams,\n id,\n (handle) => {\n handle.send(\n sseFrame({\n ops: [{ op: \"mount\", protocol, main: true, value: state }],\n syn: {\n seq: rec.seq,\n ctxSeq: 0,\n version: \"2026-09-07\",\n protocols: [{ name: protocol, version: \"2026-09-07\" }],\n },\n }),\n );\n },\n init?.signal ?? undefined,\n { \"Statewire-Lease\": \"mock-lease\" },\n );\n }\n if (url.endsWith(\"/frames\")) {\n admit(clientId, JSON.parse(String(init?.body ?? \"{}\")) as StatewireFrame);\n return Response.json({});\n }\n return new Response(null, { status: 404 });\n }) as typeof fetch;\n\n return {\n transport: StatewireHttp({\n url: `http://mock-${++mockCount}/api`,\n fetch: fakeFetch,\n }),\n commands,\n setState: (next) => {\n state = next;\n return sendEnvelope({ ops: [{ op: \"replace\", path: [], value: next }] });\n },\n applied: (seq) => sendEnvelope({ cmd: [{ seq, type: \"applied\" }] }),\n settle: (seq, payload) =>\n sendEnvelope({\n cmd: [\n {\n seq,\n type: \"result\",\n ...(payload !== undefined && { payload }),\n },\n ],\n }),\n sendEnvelope,\n finish: async (reason, message) => {\n await sendEnvelope({\n fin: { reason, ...(message !== undefined && { message }) },\n });\n finished = true;\n for (const stream of [...streams]) stream.close();\n await tick();\n },\n get clientId() {\n return clientId;\n },\n get attached() {\n return streams.size > 0;\n },\n };\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "statewire",
3
- "version": "0.18.1",
3
+ "version": "0.18.2",
4
4
  "description": "State over the wire: snapshot-first delta streaming and command submission for any state and any command",
5
5
  "license": "MIT",
6
6
  "repository": {