statewire 0.24.1 → 0.24.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/connection.d.ts +4 -0
- package/dist/client/connection.d.ts.map +1 -1
- package/dist/client/connection.js +9 -2
- package/dist/client/connection.js.map +1 -1
- package/dist/client/session.d.ts +2 -0
- package/dist/client/session.d.ts.map +1 -1
- package/dist/client/session.js +52 -11
- package/dist/client/session.js.map +1 -1
- package/dist/host/host.d.ts +1 -0
- package/dist/host/host.d.ts.map +1 -1
- package/dist/host/host.js +8 -0
- package/dist/host/host.js.map +1 -1
- package/dist/host/socket.d.ts.map +1 -1
- package/dist/host/socket.js +3 -2
- package/dist/host/socket.js.map +1 -1
- package/dist/host.d.ts +13 -7
- package/dist/host.d.ts.map +1 -1
- package/dist/storage.js +1 -1
- package/dist/transports/sse.d.ts.map +1 -1
- package/dist/transports/sse.js +2 -1
- package/dist/transports/sse.js.map +1 -1
- package/dist/wire/protocol.d.ts +6 -1
- package/dist/wire/protocol.d.ts.map +1 -1
- package/dist/wire/protocol.js +12 -4
- package/dist/wire/protocol.js.map +1 -1
- package/package.json +1 -1
- package/src/client/connection.ts +11 -0
- package/src/client/session.ts +85 -11
- package/src/host/host.ts +13 -0
- package/src/host/socket.ts +5 -2
- package/src/host.ts +14 -6
- package/src/transports/sse.ts +2 -0
- package/src/wire/protocol.ts +11 -3
package/dist/host.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,mBAAgB;AAEzC,OAAO,EAAE,eAAe,EAAE,yBAAsB;AAChD,OAAO,EAAE,gBAAgB,EAAE,uBAAoB;AAC/C,OAAO,EAAE,MAAM,EAAE,wBAAqB;AACtC,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,oBAAoB,GACrB,uBAAoB;AACrB,OAAO,EAAE,cAAc,EAAE,yBAAsB;AAE/C,yBAAiB,aAAa,CAAC;IAC7B,KAAY,IAAI,GAAG;QACjB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;QACzB,KAAK,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;QAC3C,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;KAC1C,CAAC;IAEF,KAAY,QAAQ,CAAC,MAAM,GAAG,OAAO,IAAI;QACvC,qEAAqE;QACrE,KAAK,EAAE,CAAC,GAAG,EAAE,SAAS,SAAS,CAAC,EAAE,EAAE,KAAK,IAAI,CAAC;QAC9C,uGAAuG;QACvG,QAAQ,EAAE,MAAM,MAAM,CAAC;KACxB,CAAC;IAEF,KAAY,QAAQ,GAAG;QACrB,QAAQ,EAAE,MAAM,CAAC;QACjB,GAAG,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;QAC9B,IAAI,EAAE,OAAO,CAAC;QACd,KAAK,EAAE,OAAO,CAAC;KAChB,CAAC;IAEF,KAAY,SAAS,GAAG,SAAS,CAAC,WAAW,GAAG;QAC9C,oEAAoE;QACpE,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC,EAAE,KAAK,IAAI,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;QACjE,4HAA4H;QAC5H,KAAK,EAAE,CACL,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE;YAAE,GAAG,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;YAAC,IAAI,CAAC,EAAE,OAAO,CAAA;SAAE,KAClD,IAAI,CAAC;QACV,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,SAAS,MAAM,EAAE,KAAK,IAAI,CAAC;QAC7D,SAAS,EAAE,MAAM,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC/C,cAAc,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;KACzD,CAAC;IAEF
|
|
1
|
+
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"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"}
|
package/dist/storage.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/transports/sse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,uBAAoB;AAErD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/transports/sse.ts"],"names":[],"mappings":"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"}
|
package/dist/transports/sse.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { negotiationHeaders, parseNegotiation } from "../wire/protocol.js";
|
|
2
2
|
import { timing } from "../client/timing.js";
|
|
3
|
-
import { classify, handleEnvelope, resolveHeaders, settleFin, useLink, validateRefusalSyn } from "../client/connection.js";
|
|
3
|
+
import { classify, handleEnvelope, replacing, resolveHeaders, settleFin, useLink, validateRefusalSyn } from "../client/connection.js";
|
|
4
4
|
import { createSseParser } from "./sse-parser.js";
|
|
5
5
|
import { useStatewireRemote } from "./remote.js";
|
|
6
6
|
import { flushTapSync, resource } from "@assistant-ui/tap";
|
|
@@ -55,6 +55,7 @@ const attemptStream = async (c, options, abort) => {
|
|
|
55
55
|
const headers = await resolveHeaders(options.headers, c.authStale);
|
|
56
56
|
for (const [name, value] of Object.entries(negotiationHeaders(options))) if (!headers.has(name)) headers.set(name, value);
|
|
57
57
|
c.negotiation = parseNegotiation(headers);
|
|
58
|
+
replacing(c, headers);
|
|
58
59
|
c.authStale = false;
|
|
59
60
|
const lanes = flushTapSync(() => c.ctx.takeAttachLanes());
|
|
60
61
|
const body = lanes === null ? void 0 : JSON.stringify(lanes);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse.js","names":["StatewireDocument","flushTapSync","resource","ResourceElement","createSseParser","timing","settleFin","classify","handleEnvelope","resolveHeaders","useLink","validateRefusalSyn","AttemptOutcome","Conn","StatewireCmd","StatewireEnvelope","StatewireSyn","Statewire","StatewireClient","StatewireHost","useStatewireRemote","negotiationHeaders","parseNegotiation","StatewireFrame","ctx","protocol","Encoded","cmd","method","params","meta","seq","res","StatewireRemoteProps","beforeAttach","Promise","clientId","takeAttachLanes","onHello","args","ops","syn","onOps","cmds","onCmds","onRefusal","onIdle","level","onDetach","onFinish","Fin","StatewireSubmitResult","type","ConnectionState","RemoteLink","storageFailed","error","Error","protocolFailed","connection","Connection","parked","send","frame","LEASE_HEADER","warnedNoLease","warnNoLease","console","warn","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","accept","cache","any","ok","clone","json","catch","fin","httpFin","get","getReader","cancel","addEventListener","once","removeEventListener","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 type { StatewireDocument } from \"../document\";\nimport { 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?: ({ protocol: string } & StatewireDocument.Encoded)[];\n cmd?: {\n protocol: string;\n method: string;\n params: unknown[];\n meta?: unknown;\n seq: number;\n }[];\n /** Attach only: per durable lane, the seq through which the client holds every verdict. */\n res?: { protocol: string; seq: number }[];\n};\n\nexport type StatewireRemoteProps = {\n beforeAttach?: () => void | Promise<void>;\n clientId: string;\n /** Lanes to ride the attach frame: ephemeral-lane statements, plus durable cmds on no-lease supersession; records the included entries. */\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 protocolFailed: (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\nlet warnedNoLease = false;\n\nconst warnNoLease = () => {\n if (warnedNoLease) return;\n warnedNoLease = true;\n console.warn(\n \"statewire: SSE attach returned no readable Statewire-Lease header; commands will re-attach the stream. Cross-origin hosts must expose it (Access-Control-Expose-Headers: Statewire-Lease).\",\n );\n};\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 if (c.lease === null) warnNoLease();\n const reader = res.body.getReader();\n const cancel = () => {\n void reader.cancel().catch(() => {});\n };\n abort.signal.addEventListener(\"abort\", cancel, { once: true });\n try {\n return await consume(c, reader, heartbeat, () => abort.signal.aborted);\n } finally {\n abort.signal.removeEventListener(\"abort\", cancel);\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":["StatewireDocument","flushTapSync","resource","ResourceElement","createSseParser","timing","replacing","settleFin","classify","handleEnvelope","resolveHeaders","useLink","validateRefusalSyn","AttemptOutcome","Conn","StatewireCmd","StatewireEnvelope","StatewireSyn","Statewire","StatewireClient","StatewireHost","useStatewireRemote","negotiationHeaders","parseNegotiation","StatewireFrame","ctx","protocol","Encoded","cmd","method","params","meta","seq","res","StatewireRemoteProps","beforeAttach","Promise","clientId","takeAttachLanes","onHello","args","ops","syn","onOps","cmds","onCmds","onRefusal","onIdle","level","onDetach","onFinish","Fin","StatewireSubmitResult","type","ConnectionState","RemoteLink","storageFailed","error","Error","protocolFailed","connection","Connection","parked","send","frame","LEASE_HEADER","warnedNoLease","warnNoLease","console","warn","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","accept","cache","any","ok","clone","json","catch","fin","httpFin","get","getReader","cancel","addEventListener","once","removeEventListener","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 type { StatewireDocument } from \"../document\";\nimport { 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 replacing,\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?: ({ protocol: string } & StatewireDocument.Encoded)[];\n cmd?: {\n protocol: string;\n method: string;\n params: unknown[];\n meta?: unknown;\n seq: number;\n }[];\n /** Attach only: per durable lane, the seq through which the client holds every verdict. */\n res?: { protocol: string; seq: number }[];\n};\n\nexport type StatewireRemoteProps = {\n beforeAttach?: () => void | Promise<void>;\n clientId: string;\n /** Lanes to ride the attach frame: ephemeral-lane statements, plus durable cmds on no-lease supersession; records the included entries. */\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 protocolFailed: (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\nlet warnedNoLease = false;\n\nconst warnNoLease = () => {\n if (warnedNoLease) return;\n warnedNoLease = true;\n console.warn(\n \"statewire: SSE attach returned no readable Statewire-Lease header; commands will re-attach the stream. Cross-origin hosts must expose it (Access-Control-Expose-Headers: Statewire-Lease).\",\n );\n};\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 replacing(c, 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 if (c.lease === null) warnNoLease();\n const reader = res.body.getReader();\n const cancel = () => {\n void reader.cancel().catch(() => {});\n };\n abort.signal.addEventListener(\"abort\", cancel, { once: true });\n try {\n return await consume(c, reader, heartbeat, () => abort.signal.aborted);\n } finally {\n abort.signal.removeEventListener(\"abort\", cancel);\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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"}
|
package/dist/wire/protocol.d.ts
CHANGED
|
@@ -30,9 +30,14 @@ export declare namespace StatewireProtocol {
|
|
|
30
30
|
}[];
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
|
-
export declare const WIRE_VERSION = "2026-11-
|
|
33
|
+
export declare const WIRE_VERSION = "2026-11-02";
|
|
34
34
|
/** Attach carries a results cursor and gets its verdicts gap-filled; a re-sent seq at or below the watermark is a protocol error. */
|
|
35
35
|
export declare const GAP_FILL_VERSION = "2026-11-01";
|
|
36
|
+
/** Every attach of a connection names its attacher; a host retires the attacher's earlier attaches before the hello. */
|
|
37
|
+
export declare const FENCE_VERSION = "2026-11-02";
|
|
38
|
+
/** The wire a client offers by default: the gap-fill contract and newer. */
|
|
39
|
+
export declare const CLIENT_WIRE: StatewireProtocol.Range;
|
|
40
|
+
export declare const ATTACHER_HEADER = "Statewire-Attacher";
|
|
36
41
|
/** The oldest wire contract a host serves by default (re-sent seqs re-emit their verdicts). */
|
|
37
42
|
export declare const MIN_SERVED_WIRE_VERSION = "2026-09-30";
|
|
38
43
|
export declare const DEFAULT_PROTOCOL_VERSION = "2026-09-30";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/wire/protocol.ts"],"names":[],"mappings":"AAAA,yBAAiB,iBAAiB,CAAC;IACjC,KAAY,KAAK,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7D,KAAY,KAAK,GAAG,KAAK,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACjE,KAAY,SAAS,GAAG;QACtB,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,WAAW,CAAA;SAAE,EAAE,CAAC;KACtE,CAAC;IACF,KAAY,OAAO,GAAG;QACpB,WAAW,CAAC,EAAE,KAAK,CAAC;QACpB,oBAAoB,CAAC,EAAE,SAAS,KAAK,EAAE,GAAG,GAAG,CAAC;QAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC;KAC3D,CAAC;IACF,KAAY,UAAU,GAAG,KAAK,GAAG;QAC/B,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;QAC3C,YAAY,CAAC,EAAE,SAAS;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,WAAW,CAAA;SAAE,EAAE,CAAC;KAClE,CAAC;CACH;AAED,eAAO,MAAM,YAAY,eAAe,CAAC;AACzC,qIAAqI;AACrI,eAAO,MAAM,gBAAgB,eAAe,CAAC;AAC7C,+FAA+F;AAC/F,eAAO,MAAM,uBAAuB,eAAe,CAAC;AACpD,eAAO,MAAM,wBAAwB,eAAe,CAAC;AACrD,eAAO,MAAM,gBAAgB,YAAY,CAAC;AAC1C,eAAO,MAAM,eAAe,uBAAuB,CAAC;AACpD,eAAO,MAAM,cAAc,sBAAsB,CAAC;AAClD,eAAO,MAAM,aAAa,QAA6C,CAAC;AAMxE,eAAO,MAAM,eAAe,GAAI,OAAO,OAAO,KAAG,MAWhD,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,OAAO,iBAAiB,CAAC,KAAK;;;CAQ3D,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,OAAO,MAAM,EAAE,OAAO,iBAAiB,CAAC,KAAK,YAGpE,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,SAAS,iBAAiB,CAAC,OAAO,6CAM9D,CAAC;AAEJ,eAAO,MAAM,kBAAkB,GAC7B,SAAS,iBAAiB,CAAC,OAAO,KACjC,MAAM,CAAC,MAAM,EAAE,MAAM,CAgCvB,CAAC;AA6BF,eAAO,MAAM,gBAAgB,GAC3B,SAAS,OAAO,KACf,QAAQ,CACT,IAAI,CAAC,iBAAiB,CAAC,OAAO,EAAE,aAAa,GAAG,sBAAsB,CAAC,CAwDxE,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,OAAO,OAAO,EACd,SAAS,iBAAiB,CAAC,OAAO,KACjC,iBAAiB,CAAC,SA2CpB,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,SAAS,OAAO,EAChB,MAAM,iBAAiB,CAAC,KAAK,EAC7B,aAAa,SAAS,iBAAiB,CAAC,UAAU,EAAE,KAElD,iBAAiB,CAAC,SAAS,GAC3B;IACE,GAAG,EAAE;QACH,MAAM,EAAE,gBAAgB,CAAC;QACzB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC;CA2GP,CAAC"}
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/wire/protocol.ts"],"names":[],"mappings":"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"}
|
package/dist/wire/protocol.js
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
// src/wire/protocol.ts
|
|
2
|
-
const WIRE_VERSION = "2026-11-
|
|
2
|
+
const WIRE_VERSION = "2026-11-02";
|
|
3
3
|
/** Attach carries a results cursor and gets its verdicts gap-filled; a re-sent seq at or below the watermark is a protocol error. */
|
|
4
4
|
const GAP_FILL_VERSION = "2026-11-01";
|
|
5
|
+
/** Every attach of a connection names its attacher; a host retires the attacher's earlier attaches before the hello. */
|
|
6
|
+
const FENCE_VERSION = "2026-11-02";
|
|
7
|
+
/** The wire a client offers by default: the gap-fill contract and newer. */
|
|
8
|
+
const CLIENT_WIRE = {
|
|
9
|
+
version: WIRE_VERSION,
|
|
10
|
+
minVersion: GAP_FILL_VERSION
|
|
11
|
+
};
|
|
12
|
+
const ATTACHER_HEADER = "Statewire-Attacher";
|
|
5
13
|
/** The oldest wire contract a host serves by default (re-sent seqs re-emit their verdicts). */
|
|
6
14
|
const MIN_SERVED_WIRE_VERSION = "2026-09-30";
|
|
7
15
|
const DEFAULT_PROTOCOL_VERSION = "2026-09-30";
|
|
@@ -44,7 +52,7 @@ const negotiationHeaders = (options) => {
|
|
|
44
52
|
const seen = /* @__PURE__ */ new Set();
|
|
45
53
|
if (offers !== "*" && !offers.length) invalid("protocol list must not be empty");
|
|
46
54
|
return {
|
|
47
|
-
[VERSION_HEADER]: suffix(options.wireVersion ??
|
|
55
|
+
[VERSION_HEADER]: suffix(options.wireVersion ?? CLIENT_WIRE),
|
|
48
56
|
[PROTOCOL_HEADER]: offers === "*" ? "*" : offers.map((offer) => {
|
|
49
57
|
if (!protocolToken.test(offer.name) || offer.name === "*" || seen.has(offer.name)) invalid("invalid or duplicate protocol name");
|
|
50
58
|
if (offer.optional !== void 0 && typeof offer.optional !== "boolean") invalid("optional must be boolean");
|
|
@@ -128,7 +136,7 @@ const validateSelection = (value, options) => {
|
|
|
128
136
|
if (typeof value !== "object" || value === null) return invalid("missing negotiation selection");
|
|
129
137
|
const syn = value;
|
|
130
138
|
const version = validateVersion(syn.version);
|
|
131
|
-
if (!inRange(version, options.wireVersion ??
|
|
139
|
+
if (!inRange(version, options.wireVersion ?? CLIENT_WIRE)) invalid("selected wire version is outside offer");
|
|
132
140
|
if (!Array.isArray(syn.protocols)) return invalid("syn.protocols must be an array");
|
|
133
141
|
const offers = protocolOffers(options);
|
|
134
142
|
const seen = /* @__PURE__ */ new Map();
|
|
@@ -241,6 +249,6 @@ const negotiateProtocols = (headers, wire, definitions) => {
|
|
|
241
249
|
})
|
|
242
250
|
};
|
|
243
251
|
};
|
|
244
|
-
export { DEFAULT_PROTOCOL, DEFAULT_PROTOCOL_VERSION, GAP_FILL_VERSION, MIN_SERVED_WIRE_VERSION, PROTOCOL_HEADER, VERSION_HEADER, WIRE_VERSION, inRange, negotiateProtocols, negotiationHeaders, parseNegotiation, protocolOffers, protocolToken, validateRange, validateSelection, validateVersion };
|
|
252
|
+
export { ATTACHER_HEADER, CLIENT_WIRE, DEFAULT_PROTOCOL, DEFAULT_PROTOCOL_VERSION, FENCE_VERSION, GAP_FILL_VERSION, MIN_SERVED_WIRE_VERSION, PROTOCOL_HEADER, VERSION_HEADER, WIRE_VERSION, inRange, negotiateProtocols, negotiationHeaders, parseNegotiation, protocolOffers, protocolToken, validateRange, validateSelection, validateVersion };
|
|
245
253
|
|
|
246
254
|
//# sourceMappingURL=protocol.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.js","names":["StatewireProtocol","Range","version","minVersion","Offer","name","optional","Selection","protocols","record","Options","wireVersion","applicationProtocols","protocol","keyPrefixes","Readonly","Record","Definition","requires","subprotocols","WIRE_VERSION","GAP_FILL_VERSION","MIN_SERVED_WIRE_VERSION","DEFAULT_PROTOCOL_VERSION","DEFAULT_PROTOCOL","PROTOCOL_HEADER","VERSION_HEADER","protocolToken","invalid","message","Error","validateVersion","value","test","parsed","Date","Number","isFinite","getTime","toISOString","slice","startsWith","validateRange","range","undefined","inRange","checked","protocolOffers","options","negotiationHeaders","suffix","offers","seen","Set","length","map","offer","has","add","join","parseItem","input","atom","String","raw","text","trim","match","RegExp","exec","quoted","decode","replace","params","Map","rest","parameter","key","set","parseNegotiation","headers","Headers","Required","Pick","wire","get","application","item","keys","some","k","names","split","includes","validateSelection","syn","Array","isArray","entry","parent","lastIndexOf","child","find","o","negotiateProtocols","definitions","fin","reason","code","status","payload","supported","d","failure","const","request","ReturnType","error","sort","registry","size","definition","children","offered","solve","active","dependency","Object","result","ranges","flatMap","lower","r","at","upper","selected","filter"],"sources":["../../src/wire/protocol.ts"],"sourcesContent":["export namespace StatewireProtocol {\n export type Range = { version: string; minVersion?: string };\n export type Offer = Range & { name: string; optional?: boolean };\n export type Selection = {\n version: string;\n protocols: { name: string; version: string; record?: \"ephemeral\" }[];\n };\n export type Options = {\n wireVersion?: Range;\n applicationProtocols?: readonly Offer[] | \"*\";\n protocol?: string;\n keyPrefixes?: Readonly<Record<string, readonly string[]>>;\n };\n export type Definition = Range & {\n name: string;\n requires?: Readonly<Record<string, Range>>;\n subprotocols?: readonly { name: string; record?: \"ephemeral\" }[];\n };\n}\n\nexport const WIRE_VERSION = \"2026-11-01\";\n/** Attach carries a results cursor and gets its verdicts gap-filled; a re-sent seq at or below the watermark is a protocol error. */\nexport const GAP_FILL_VERSION = \"2026-11-01\";\n/** The oldest wire contract a host serves by default (re-sent seqs re-emit their verdicts). */\nexport const MIN_SERVED_WIRE_VERSION = \"2026-09-30\";\nexport const DEFAULT_PROTOCOL_VERSION = \"2026-09-30\";\nexport const DEFAULT_PROTOCOL = \"default\";\nexport const PROTOCOL_HEADER = \"Statewire-Protocol\";\nexport const VERSION_HEADER = \"Statewire-Version\";\nexport const protocolToken = /^[A-Za-z*][A-Za-z0-9!#$%&'*+.^_`|~:/-]*$/;\n\nconst invalid = (message: string): never => {\n throw new Error(`statewire: protocol error: ${message}`);\n};\n\nexport const validateVersion = (value: unknown): string => {\n if (typeof value !== \"string\" || !/^\\d{4}-\\d{2}-\\d{2}$/.test(value))\n return invalid(\"version must be a YYYY-MM-DD date\");\n const parsed = new Date(`${value}T00:00:00.000Z`);\n if (\n !Number.isFinite(parsed.getTime()) ||\n parsed.toISOString().slice(0, 10) !== value ||\n value.startsWith(\"0000\")\n )\n return invalid(\"invalid version date\");\n return value;\n};\n\nexport const validateRange = (range: StatewireProtocol.Range) => {\n const version = validateVersion(range.version);\n const minVersion =\n range.minVersion === undefined\n ? version\n : validateVersion(range.minVersion);\n if (minVersion > version) invalid(\"min-version exceeds version\");\n return { version, minVersion };\n};\n\nexport const inRange = (value: string, range: StatewireProtocol.Range) => {\n const checked = validateRange(range);\n return value >= checked.minVersion && value <= checked.version;\n};\n\nexport const protocolOffers = (options: StatewireProtocol.Options) =>\n options.applicationProtocols ?? [\n {\n name: options.protocol ?? DEFAULT_PROTOCOL,\n version: DEFAULT_PROTOCOL_VERSION,\n },\n ];\n\nexport const negotiationHeaders = (\n options: StatewireProtocol.Options,\n): Record<string, string> => {\n const suffix = (range: StatewireProtocol.Range) => {\n const { version, minVersion } = validateRange(range);\n return `\"${version}\"${minVersion === version ? \"\" : `; min-version=\"${minVersion}\"`}`;\n };\n const offers = protocolOffers(options);\n const seen = new Set<string>();\n if (offers !== \"*\" && !offers.length)\n invalid(\"protocol list must not be empty\");\n return {\n [VERSION_HEADER]: suffix(options.wireVersion ?? { version: WIRE_VERSION }),\n [PROTOCOL_HEADER]:\n offers === \"*\"\n ? \"*\"\n : offers\n .map((offer) => {\n if (\n !protocolToken.test(offer.name) ||\n offer.name === \"*\" ||\n seen.has(offer.name)\n )\n invalid(\"invalid or duplicate protocol name\");\n if (\n offer.optional !== undefined &&\n typeof offer.optional !== \"boolean\"\n )\n invalid(\"optional must be boolean\");\n seen.add(offer.name);\n return `${offer.name}; version=${suffix(offer)}${offer.optional ? \"; optional\" : \"\"}`;\n })\n .join(\", \"),\n };\n};\n\nconst parseItem = (input: string) => {\n const atom = String.raw`\"(?:[\\x20-\\x21\\x23-\\x5b\\x5d-\\x7e]|\\\\[\"\\\\])*\"|[A-Za-z*][A-Za-z0-9!#$%&'*+.^_\\x60|~:/-]*|\\?[01]`;\n const text = input.trim();\n const match = new RegExp(`^(?:${atom})`).exec(text);\n if (!match) return invalid(\"invalid structured item\");\n const quoted = match[0].startsWith('\"');\n const decode = (raw: string) => raw.slice(1, -1).replace(/\\\\([\"\\\\])/g, \"$1\");\n const value = quoted ? decode(match[0]) : match[0];\n const params = new Map<string, string | boolean>();\n let rest = text.slice(match[0].length);\n while (rest) {\n const parameter = new RegExp(\n `^; *([a-z*][a-z0-9_.*-]*)(?:=(${atom}))?`,\n ).exec(rest);\n if (!parameter) return invalid(\"invalid structured parameters\");\n const key = parameter[1]!;\n const raw = parameter[2];\n if (params.has(key)) invalid(\"duplicate parameter\");\n if (raw === undefined || raw === \"?1\") params.set(key, true);\n else if (raw === \"?0\") params.set(key, false);\n else if (raw.startsWith('\"')) params.set(key, decode(raw));\n else invalid(\"parameter must be string or boolean\");\n rest = rest.slice(parameter[0].length);\n }\n return { value, quoted, params };\n};\n\nexport const parseNegotiation = (\n headers: Headers,\n): Required<\n Pick<StatewireProtocol.Options, \"wireVersion\" | \"applicationProtocols\">\n> => {\n const wire = headers.get(VERSION_HEADER);\n const application = headers.get(PROTOCOL_HEADER);\n if (wire === null || application === null)\n return invalid(\"negotiation fields are required\");\n const item = parseItem(wire);\n if (!item.quoted || [...item.params.keys()].some((k) => k !== \"min-version\"))\n invalid(\"invalid Statewire-Version\");\n const minVersion = item.params.get(\"min-version\");\n if (minVersion !== undefined && typeof minVersion !== \"string\")\n return invalid(\"min-version must be a string\");\n const wireVersion = validateRange({\n version: item.value,\n ...(minVersion === undefined ? {} : { minVersion }),\n });\n if (application.trim() === \"*\")\n return { wireVersion, applicationProtocols: \"*\" };\n const names = new Set<string>();\n const applicationProtocols = application\n .split(\",\")\n .map((text): StatewireProtocol.Offer => {\n const { value: name, quoted, params } = parseItem(text);\n if (\n quoted ||\n !protocolToken.test(name) ||\n name === \"*\" ||\n names.has(name)\n )\n invalid(\"invalid or duplicate protocol name\");\n names.add(name);\n if (\n [...params.keys()].some(\n (k) => ![\"version\", \"min-version\", \"optional\"].includes(k),\n )\n )\n invalid(\"unsupported parameter\");\n const version = params.get(\"version\");\n const minVersion = params.get(\"min-version\");\n const optional = params.get(\"optional\") ?? false;\n if (\n typeof version !== \"string\" ||\n (minVersion !== undefined && typeof minVersion !== \"string\") ||\n typeof optional !== \"boolean\"\n )\n return invalid(\"invalid protocol parameters\");\n return {\n name,\n ...validateRange({\n version,\n ...(minVersion === undefined ? {} : { minVersion }),\n }),\n optional,\n };\n });\n return { wireVersion, applicationProtocols };\n};\n\nexport const validateSelection = (\n value: unknown,\n options: StatewireProtocol.Options,\n): StatewireProtocol.Selection => {\n if (typeof value !== \"object\" || value === null)\n return invalid(\"missing negotiation selection\");\n const syn = value as Record<string, unknown>;\n const version = validateVersion(syn.version);\n if (!inRange(version, options.wireVersion ?? { version: WIRE_VERSION }))\n invalid(\"selected wire version is outside offer\");\n if (!Array.isArray(syn.protocols))\n return invalid(\"syn.protocols must be an array\");\n const offers = protocolOffers(options);\n const seen = new Map<string, string>();\n const protocols = syn.protocols.map((entry: unknown) => {\n if (typeof entry !== \"object\" || entry === null)\n return invalid(\"invalid selected protocol\");\n const item = entry as Record<string, unknown>;\n const name = item.name;\n if (\n typeof name !== \"string\" ||\n !protocolToken.test(name) ||\n name === \"*\" ||\n seen.has(name)\n )\n return invalid(\"invalid selected protocol name\");\n const version = validateVersion(item.version);\n if (item.record !== undefined && item.record !== \"ephemeral\")\n invalid(\"invalid selected protocol record\");\n const record = item.record as \"ephemeral\" | undefined;\n const parent = name.slice(0, name.lastIndexOf(\"/\"));\n const child = parent !== \"\" && seen.has(parent);\n if (child) {\n if (seen.get(parent) !== version)\n invalid(\"subprotocol version differs from its parent\");\n } else if (offers !== \"*\") {\n const offer = offers.find((o) => o.name === name);\n if (!offer || !inRange(version, offer))\n invalid(\"selected protocol is outside offer\");\n }\n seen.set(name, version);\n return { name, version, ...(record !== undefined && { record }) };\n });\n if (offers !== \"*\" && offers.some((o) => !o.optional && !seen.has(o.name)))\n invalid(\"required protocol missing from selection\");\n return { version, protocols };\n};\n\nexport const negotiateProtocols = (\n headers: Headers,\n wire: StatewireProtocol.Range,\n definitions: readonly StatewireProtocol.Definition[],\n):\n | StatewireProtocol.Selection\n | {\n fin: {\n reason: \"protocol-error\";\n code: string;\n status: number;\n message: string;\n payload?: unknown;\n };\n } => {\n const supported = {\n ...validateRange(wire),\n protocols: definitions.map((d) => ({ name: d.name, ...validateRange(d) })),\n };\n const failure = (code: string, message: string) => ({\n fin: {\n reason: \"protocol-error\" as const,\n code,\n message,\n status: code === \"invalid-negotiation\" ? 400 : 406,\n ...(code === \"invalid-negotiation\" ? {} : { payload: supported }),\n },\n });\n let request: ReturnType<typeof parseNegotiation>;\n try {\n request = parseNegotiation(headers);\n } catch (error) {\n return failure(\n \"invalid-negotiation\",\n error instanceof Error ? error.message : \"invalid negotiation\",\n );\n }\n const version = [wire.version, request.wireVersion.version].sort()[0]!;\n if (!inRange(version, wire) || !inRange(version, request.wireVersion))\n return failure(\"unsupported-version\", \"Statewire versions do not overlap\");\n const offers =\n request.applicationProtocols === \"*\"\n ? definitions.map((d) => ({\n name: d.name,\n version: d.version,\n optional: false,\n }))\n : request.applicationProtocols;\n const registry = new Map(definitions.map((d) => [d.name, d]));\n if (registry.size !== definitions.length)\n throw new Error(\"statewire: duplicate registered protocol\");\n for (const definition of definitions) {\n const children = new Set<string>();\n for (const child of definition.subprotocols ?? []) {\n if (\n !protocolToken.test(child.name) ||\n child.name.includes(\"/\") ||\n children.has(child.name) ||\n (child.record !== undefined && child.record !== \"ephemeral\")\n )\n throw new Error(\"statewire: invalid subprotocol registration\");\n children.add(child.name);\n }\n }\n const offered = new Map(offers.map((o) => [o.name, o]));\n const solve = (names: Set<string>) => {\n const active = new Set(names);\n for (const name of active) {\n const definition = registry.get(name);\n if (!definition || !offered.has(name)) return undefined;\n for (const dependency of Object.keys(definition.requires ?? {}))\n active.add(dependency);\n }\n const result = new Map<string, string>();\n for (const name of active) {\n const ranges = [\n registry.get(name)!,\n offered.get(name)!,\n ...[...active].flatMap((parent) => {\n const dependency = registry.get(parent)!.requires?.[name];\n return dependency ? [dependency] : [];\n }),\n ].map(validateRange);\n const lower = ranges\n .map((r) => r.minVersion)\n .sort()\n .at(-1)!;\n const upper = ranges.map((r) => r.version).sort()[0]!;\n if (lower > upper) return undefined;\n result.set(name, upper);\n }\n return result;\n };\n let selected = solve(\n new Set(offers.filter((o) => !o.optional).map((o) => o.name)),\n );\n if (!selected)\n return failure(\n \"unsupported-protocol\",\n \"required protocol combination is unsupported\",\n );\n for (const offer of offers) {\n if (offer.optional)\n selected = solve(new Set([...selected.keys(), offer.name])) ?? selected;\n }\n return {\n version,\n protocols: offers.flatMap((o) => {\n const version = selected!.get(o.name);\n if (version === undefined) return [];\n return [\n { name: o.name, version },\n ...(registry.get(o.name)!.subprotocols ?? []).map((child) => ({\n name: `${o.name}/${child.name}`,\n version,\n ...(child.record !== undefined && { record: child.record }),\n })),\n ];\n }),\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"protocol.js","names":["StatewireProtocol","Range","version","minVersion","Offer","name","optional","Selection","protocols","record","Options","wireVersion","applicationProtocols","protocol","keyPrefixes","Readonly","Record","Definition","requires","subprotocols","WIRE_VERSION","GAP_FILL_VERSION","FENCE_VERSION","CLIENT_WIRE","ATTACHER_HEADER","MIN_SERVED_WIRE_VERSION","DEFAULT_PROTOCOL_VERSION","DEFAULT_PROTOCOL","PROTOCOL_HEADER","VERSION_HEADER","protocolToken","invalid","message","Error","validateVersion","value","test","parsed","Date","Number","isFinite","getTime","toISOString","slice","startsWith","validateRange","range","undefined","inRange","checked","protocolOffers","options","negotiationHeaders","suffix","offers","seen","Set","length","map","offer","has","add","join","parseItem","input","atom","String","raw","text","trim","match","RegExp","exec","quoted","decode","replace","params","Map","rest","parameter","key","set","parseNegotiation","headers","Headers","Required","Pick","wire","get","application","item","keys","some","k","names","split","includes","validateSelection","syn","Array","isArray","entry","parent","lastIndexOf","child","find","o","negotiateProtocols","definitions","fin","reason","code","status","payload","supported","d","failure","const","request","ReturnType","error","sort","registry","size","definition","children","offered","solve","active","dependency","Object","result","ranges","flatMap","lower","r","at","upper","selected","filter"],"sources":["../../src/wire/protocol.ts"],"sourcesContent":["export namespace StatewireProtocol {\n export type Range = { version: string; minVersion?: string };\n export type Offer = Range & { name: string; optional?: boolean };\n export type Selection = {\n version: string;\n protocols: { name: string; version: string; record?: \"ephemeral\" }[];\n };\n export type Options = {\n wireVersion?: Range;\n applicationProtocols?: readonly Offer[] | \"*\";\n protocol?: string;\n keyPrefixes?: Readonly<Record<string, readonly string[]>>;\n };\n export type Definition = Range & {\n name: string;\n requires?: Readonly<Record<string, Range>>;\n subprotocols?: readonly { name: string; record?: \"ephemeral\" }[];\n };\n}\n\nexport const WIRE_VERSION = \"2026-11-02\";\n/** Attach carries a results cursor and gets its verdicts gap-filled; a re-sent seq at or below the watermark is a protocol error. */\nexport const GAP_FILL_VERSION = \"2026-11-01\";\n/** Every attach of a connection names its attacher; a host retires the attacher's earlier attaches before the hello. */\nexport const FENCE_VERSION = \"2026-11-02\";\n/** The wire a client offers by default: the gap-fill contract and newer. */\nexport const CLIENT_WIRE: StatewireProtocol.Range = {\n version: WIRE_VERSION,\n minVersion: GAP_FILL_VERSION,\n};\nexport const ATTACHER_HEADER = \"Statewire-Attacher\";\n/** The oldest wire contract a host serves by default (re-sent seqs re-emit their verdicts). */\nexport const MIN_SERVED_WIRE_VERSION = \"2026-09-30\";\nexport const DEFAULT_PROTOCOL_VERSION = \"2026-09-30\";\nexport const DEFAULT_PROTOCOL = \"default\";\nexport const PROTOCOL_HEADER = \"Statewire-Protocol\";\nexport const VERSION_HEADER = \"Statewire-Version\";\nexport const protocolToken = /^[A-Za-z*][A-Za-z0-9!#$%&'*+.^_`|~:/-]*$/;\n\nconst invalid = (message: string): never => {\n throw new Error(`statewire: protocol error: ${message}`);\n};\n\nexport const validateVersion = (value: unknown): string => {\n if (typeof value !== \"string\" || !/^\\d{4}-\\d{2}-\\d{2}$/.test(value))\n return invalid(\"version must be a YYYY-MM-DD date\");\n const parsed = new Date(`${value}T00:00:00.000Z`);\n if (\n !Number.isFinite(parsed.getTime()) ||\n parsed.toISOString().slice(0, 10) !== value ||\n value.startsWith(\"0000\")\n )\n return invalid(\"invalid version date\");\n return value;\n};\n\nexport const validateRange = (range: StatewireProtocol.Range) => {\n const version = validateVersion(range.version);\n const minVersion =\n range.minVersion === undefined\n ? version\n : validateVersion(range.minVersion);\n if (minVersion > version) invalid(\"min-version exceeds version\");\n return { version, minVersion };\n};\n\nexport const inRange = (value: string, range: StatewireProtocol.Range) => {\n const checked = validateRange(range);\n return value >= checked.minVersion && value <= checked.version;\n};\n\nexport const protocolOffers = (options: StatewireProtocol.Options) =>\n options.applicationProtocols ?? [\n {\n name: options.protocol ?? DEFAULT_PROTOCOL,\n version: DEFAULT_PROTOCOL_VERSION,\n },\n ];\n\nexport const negotiationHeaders = (\n options: StatewireProtocol.Options,\n): Record<string, string> => {\n const suffix = (range: StatewireProtocol.Range) => {\n const { version, minVersion } = validateRange(range);\n return `\"${version}\"${minVersion === version ? \"\" : `; min-version=\"${minVersion}\"`}`;\n };\n const offers = protocolOffers(options);\n const seen = new Set<string>();\n if (offers !== \"*\" && !offers.length)\n invalid(\"protocol list must not be empty\");\n return {\n [VERSION_HEADER]: suffix(options.wireVersion ?? CLIENT_WIRE),\n [PROTOCOL_HEADER]:\n offers === \"*\"\n ? \"*\"\n : offers\n .map((offer) => {\n if (\n !protocolToken.test(offer.name) ||\n offer.name === \"*\" ||\n seen.has(offer.name)\n )\n invalid(\"invalid or duplicate protocol name\");\n if (\n offer.optional !== undefined &&\n typeof offer.optional !== \"boolean\"\n )\n invalid(\"optional must be boolean\");\n seen.add(offer.name);\n return `${offer.name}; version=${suffix(offer)}${offer.optional ? \"; optional\" : \"\"}`;\n })\n .join(\", \"),\n };\n};\n\nconst parseItem = (input: string) => {\n const atom = String.raw`\"(?:[\\x20-\\x21\\x23-\\x5b\\x5d-\\x7e]|\\\\[\"\\\\])*\"|[A-Za-z*][A-Za-z0-9!#$%&'*+.^_\\x60|~:/-]*|\\?[01]`;\n const text = input.trim();\n const match = new RegExp(`^(?:${atom})`).exec(text);\n if (!match) return invalid(\"invalid structured item\");\n const quoted = match[0].startsWith('\"');\n const decode = (raw: string) => raw.slice(1, -1).replace(/\\\\([\"\\\\])/g, \"$1\");\n const value = quoted ? decode(match[0]) : match[0];\n const params = new Map<string, string | boolean>();\n let rest = text.slice(match[0].length);\n while (rest) {\n const parameter = new RegExp(\n `^; *([a-z*][a-z0-9_.*-]*)(?:=(${atom}))?`,\n ).exec(rest);\n if (!parameter) return invalid(\"invalid structured parameters\");\n const key = parameter[1]!;\n const raw = parameter[2];\n if (params.has(key)) invalid(\"duplicate parameter\");\n if (raw === undefined || raw === \"?1\") params.set(key, true);\n else if (raw === \"?0\") params.set(key, false);\n else if (raw.startsWith('\"')) params.set(key, decode(raw));\n else invalid(\"parameter must be string or boolean\");\n rest = rest.slice(parameter[0].length);\n }\n return { value, quoted, params };\n};\n\nexport const parseNegotiation = (\n headers: Headers,\n): Required<\n Pick<StatewireProtocol.Options, \"wireVersion\" | \"applicationProtocols\">\n> => {\n const wire = headers.get(VERSION_HEADER);\n const application = headers.get(PROTOCOL_HEADER);\n if (wire === null || application === null)\n return invalid(\"negotiation fields are required\");\n const item = parseItem(wire);\n if (!item.quoted || [...item.params.keys()].some((k) => k !== \"min-version\"))\n invalid(\"invalid Statewire-Version\");\n const minVersion = item.params.get(\"min-version\");\n if (minVersion !== undefined && typeof minVersion !== \"string\")\n return invalid(\"min-version must be a string\");\n const wireVersion = validateRange({\n version: item.value,\n ...(minVersion === undefined ? {} : { minVersion }),\n });\n if (application.trim() === \"*\")\n return { wireVersion, applicationProtocols: \"*\" };\n const names = new Set<string>();\n const applicationProtocols = application\n .split(\",\")\n .map((text): StatewireProtocol.Offer => {\n const { value: name, quoted, params } = parseItem(text);\n if (\n quoted ||\n !protocolToken.test(name) ||\n name === \"*\" ||\n names.has(name)\n )\n invalid(\"invalid or duplicate protocol name\");\n names.add(name);\n if (\n [...params.keys()].some(\n (k) => ![\"version\", \"min-version\", \"optional\"].includes(k),\n )\n )\n invalid(\"unsupported parameter\");\n const version = params.get(\"version\");\n const minVersion = params.get(\"min-version\");\n const optional = params.get(\"optional\") ?? false;\n if (\n typeof version !== \"string\" ||\n (minVersion !== undefined && typeof minVersion !== \"string\") ||\n typeof optional !== \"boolean\"\n )\n return invalid(\"invalid protocol parameters\");\n return {\n name,\n ...validateRange({\n version,\n ...(minVersion === undefined ? {} : { minVersion }),\n }),\n optional,\n };\n });\n return { wireVersion, applicationProtocols };\n};\n\nexport const validateSelection = (\n value: unknown,\n options: StatewireProtocol.Options,\n): StatewireProtocol.Selection => {\n if (typeof value !== \"object\" || value === null)\n return invalid(\"missing negotiation selection\");\n const syn = value as Record<string, unknown>;\n const version = validateVersion(syn.version);\n if (!inRange(version, options.wireVersion ?? CLIENT_WIRE))\n invalid(\"selected wire version is outside offer\");\n if (!Array.isArray(syn.protocols))\n return invalid(\"syn.protocols must be an array\");\n const offers = protocolOffers(options);\n const seen = new Map<string, string>();\n const protocols = syn.protocols.map((entry: unknown) => {\n if (typeof entry !== \"object\" || entry === null)\n return invalid(\"invalid selected protocol\");\n const item = entry as Record<string, unknown>;\n const name = item.name;\n if (\n typeof name !== \"string\" ||\n !protocolToken.test(name) ||\n name === \"*\" ||\n seen.has(name)\n )\n return invalid(\"invalid selected protocol name\");\n const version = validateVersion(item.version);\n if (item.record !== undefined && item.record !== \"ephemeral\")\n invalid(\"invalid selected protocol record\");\n const record = item.record as \"ephemeral\" | undefined;\n const parent = name.slice(0, name.lastIndexOf(\"/\"));\n const child = parent !== \"\" && seen.has(parent);\n if (child) {\n if (seen.get(parent) !== version)\n invalid(\"subprotocol version differs from its parent\");\n } else if (offers !== \"*\") {\n const offer = offers.find((o) => o.name === name);\n if (!offer || !inRange(version, offer))\n invalid(\"selected protocol is outside offer\");\n }\n seen.set(name, version);\n return { name, version, ...(record !== undefined && { record }) };\n });\n if (offers !== \"*\" && offers.some((o) => !o.optional && !seen.has(o.name)))\n invalid(\"required protocol missing from selection\");\n return { version, protocols };\n};\n\nexport const negotiateProtocols = (\n headers: Headers,\n wire: StatewireProtocol.Range,\n definitions: readonly StatewireProtocol.Definition[],\n):\n | StatewireProtocol.Selection\n | {\n fin: {\n reason: \"protocol-error\";\n code: string;\n status: number;\n message: string;\n payload?: unknown;\n };\n } => {\n const supported = {\n ...validateRange(wire),\n protocols: definitions.map((d) => ({ name: d.name, ...validateRange(d) })),\n };\n const failure = (code: string, message: string) => ({\n fin: {\n reason: \"protocol-error\" as const,\n code,\n message,\n status: code === \"invalid-negotiation\" ? 400 : 406,\n ...(code === \"invalid-negotiation\" ? {} : { payload: supported }),\n },\n });\n let request: ReturnType<typeof parseNegotiation>;\n try {\n request = parseNegotiation(headers);\n } catch (error) {\n return failure(\n \"invalid-negotiation\",\n error instanceof Error ? error.message : \"invalid negotiation\",\n );\n }\n const version = [wire.version, request.wireVersion.version].sort()[0]!;\n if (!inRange(version, wire) || !inRange(version, request.wireVersion))\n return failure(\"unsupported-version\", \"Statewire versions do not overlap\");\n const offers =\n request.applicationProtocols === \"*\"\n ? definitions.map((d) => ({\n name: d.name,\n version: d.version,\n optional: false,\n }))\n : request.applicationProtocols;\n const registry = new Map(definitions.map((d) => [d.name, d]));\n if (registry.size !== definitions.length)\n throw new Error(\"statewire: duplicate registered protocol\");\n for (const definition of definitions) {\n const children = new Set<string>();\n for (const child of definition.subprotocols ?? []) {\n if (\n !protocolToken.test(child.name) ||\n child.name.includes(\"/\") ||\n children.has(child.name) ||\n (child.record !== undefined && child.record !== \"ephemeral\")\n )\n throw new Error(\"statewire: invalid subprotocol registration\");\n children.add(child.name);\n }\n }\n const offered = new Map(offers.map((o) => [o.name, o]));\n const solve = (names: Set<string>) => {\n const active = new Set(names);\n for (const name of active) {\n const definition = registry.get(name);\n if (!definition || !offered.has(name)) return undefined;\n for (const dependency of Object.keys(definition.requires ?? {}))\n active.add(dependency);\n }\n const result = new Map<string, string>();\n for (const name of active) {\n const ranges = [\n registry.get(name)!,\n offered.get(name)!,\n ...[...active].flatMap((parent) => {\n const dependency = registry.get(parent)!.requires?.[name];\n return dependency ? [dependency] : [];\n }),\n ].map(validateRange);\n const lower = ranges\n .map((r) => r.minVersion)\n .sort()\n .at(-1)!;\n const upper = ranges.map((r) => r.version).sort()[0]!;\n if (lower > upper) return undefined;\n result.set(name, upper);\n }\n return result;\n };\n let selected = solve(\n new Set(offers.filter((o) => !o.optional).map((o) => o.name)),\n );\n if (!selected)\n return failure(\n \"unsupported-protocol\",\n \"required protocol combination is unsupported\",\n );\n for (const offer of offers) {\n if (offer.optional)\n selected = solve(new Set([...selected.keys(), offer.name])) ?? selected;\n }\n return {\n version,\n protocols: offers.flatMap((o) => {\n const version = selected!.get(o.name);\n if (version === undefined) return [];\n return [\n { name: o.name, version },\n ...(registry.get(o.name)!.subprotocols ?? []).map((child) => ({\n name: `${o.name}/${child.name}`,\n version,\n ...(child.record !== undefined && { record: child.record }),\n })),\n ];\n }),\n };\n};\n"],"mappings":";AAoBA,MAAaoB,eAAe;;AAE5B,MAAaC,mBAAmB;;AAEhC,MAAaC,gBAAgB;;AAE7B,MAAaC,cAAuC;CAClDrB,SAASkB;CACTjB,YAAYkB;AACd;AACA,MAAaG,kBAAkB;;AAE/B,MAAaC,0BAA0B;AACvC,MAAaC,2BAA2B;AACxC,MAAaC,mBAAmB;AAChC,MAAaC,kBAAkB;AAC/B,MAAaC,iBAAiB;AAC9B,MAAaC,gBAAgB;AAE7B,MAAMC,WAAWC,YAA2B;CAC1C,MAAM,IAAIC,MAAM,8BAA8BD,SAAS;AACzD;AAEA,MAAaE,mBAAmBC,UAA2B;CACzD,IAAI,OAAOA,UAAU,YAAY,CAAC,sBAAsBC,KAAKD,KAAK,GAChE,OAAOJ,QAAQ,mCAAmC;CACpD,MAAMM,yBAAS,IAAIC,KAAK,GAAGH,MAAK,eAAgB;CAChD,IACE,CAACI,OAAOC,SAASH,OAAOI,QAAQ,CAAC,KACjCJ,OAAOK,YAAY,CAAC,CAACC,MAAM,GAAG,EAAE,MAAMR,SACtCA,MAAMS,WAAW,MAAM,GAEvB,OAAOb,QAAQ,sBAAsB;CACvC,OAAOI;AACT;AAEA,MAAaU,iBAAiBC,UAAmC;CAC/D,MAAM5C,UAAUgC,gBAAgBY,MAAM5C,OAAO;CAC7C,MAAMC,aACJ2C,MAAM3C,eAAe4C,KAAAA,IACjB7C,UACAgC,gBAAgBY,MAAM3C,UAAU;CACtC,IAAIA,aAAaD,SAAS6B,QAAQ,6BAA6B;CAC/D,OAAO;EAAE7B;EAASC;CAAW;AAC/B;AAEA,MAAa6C,WAAWb,OAAeW,UAAmC;CACxE,MAAMG,UAAUJ,cAAcC,KAAK;CACnC,OAAOX,SAASc,QAAQ9C,cAAcgC,SAASc,QAAQ/C;AACzD;AAEA,MAAagD,kBAAkBC,YAC7BA,QAAQvC,wBAAwB,CAC9B;CACEP,MAAM8C,QAAQtC,YAAAA;CACdX,SAAAA;AACF,CAAC;AAGL,MAAakD,sBACXD,YAC2B;CAC3B,MAAME,UAAUP,UAAmC;EACjD,MAAM,EAAE5C,SAASC,eAAe0C,cAAcC,KAAK;EACnD,OAAO,IAAI5C,QAAO,GAAIC,eAAeD,UAAU,KAAK,kBAAkBC,WAAU;CAClF;CACA,MAAMmD,SAASJ,eAAeC,OAAO;CACrC,MAAMI,uBAAO,IAAIC,IAAY;CAC7B,IAAIF,WAAW,OAAO,CAACA,OAAOG,QAC5B1B,QAAQ,iCAAiC;CAC3C,OAAO;GACJF,iBAAiBwB,OAAOF,QAAQxC,eAAeY,WAAW;GAC1DK,kBACC0B,WAAW,MACP,MACAA,OACGI,KAAKC,UAAU;GACd,IACE,CAAC7B,cAAcM,KAAKuB,MAAMtD,IAAI,KAC9BsD,MAAMtD,SAAS,OACfkD,KAAKK,IAAID,MAAMtD,IAAI,GAEnB0B,QAAQ,oCAAoC;GAC9C,IACE4B,MAAMrD,aAAayC,KAAAA,KACnB,OAAOY,MAAMrD,aAAa,WAE1ByB,QAAQ,0BAA0B;GACpCwB,KAAKM,IAAIF,MAAMtD,IAAI;GACnB,OAAO,GAAGsD,MAAMtD,KAAI,YAAagD,OAAOM,KAAK,IAAIA,MAAMrD,WAAW,eAAe;EACnF,CAAC,CAAC,CACDwD,KAAK,IAAI;CACpB;AACF;AAEA,MAAMC,aAAaC,UAAkB;CACnC,MAAMC,OAAOC,OAAOC,GAAG;CACvB,MAAMC,OAAOJ,MAAMK,KAAK;CACxB,MAAMC,QAAQ,IAAIC,OAAO,OAAON,KAAI,EAAG,CAAC,CAACO,KAAKJ,IAAI;CAClD,IAAI,CAACE,OAAO,OAAOvC,QAAQ,yBAAyB;CACpD,MAAM0C,SAASH,MAAM,EAAE,CAAC1B,WAAW,IAAG;CACtC,MAAM8B,UAAUP,QAAgBA,IAAIxB,MAAM,GAAG,EAAE,CAAC,CAACgC,QAAQ,cAAc,IAAI;CAC3E,MAAMxC,QAAQsC,SAASC,OAAOJ,MAAM,EAAE,IAAIA,MAAM;CAChD,MAAMM,yBAAS,IAAIC,IAA8B;CACjD,IAAIC,OAAOV,KAAKzB,MAAM2B,MAAM,EAAE,CAACb,MAAM;CACrC,OAAOqB,MAAM;EACX,MAAMC,YAAY,IAAIR,OACpB,iCAAiCN,KAAI,IACvC,CAAC,CAACO,KAAKM,IAAI;EACX,IAAI,CAACC,WAAW,OAAOhD,QAAQ,+BAA+B;EAC9D,MAAMiD,MAAMD,UAAU;EACtB,MAAMZ,MAAMY,UAAU;EACtB,IAAIH,OAAOhB,IAAIoB,GAAG,GAAGjD,QAAQ,qBAAqB;EAClD,IAAIoC,QAAQpB,KAAAA,KAAaoB,QAAQ,MAAMS,OAAOK,IAAID,KAAK,IAAI;OACtD,IAAIb,QAAQ,MAAMS,OAAOK,IAAID,KAAK,KAAK;OACvC,IAAIb,IAAIvB,WAAW,IAAG,GAAGgC,OAAOK,IAAID,KAAKN,OAAOP,GAAG,CAAC;OACpDpC,QAAQ,qCAAqC;EAClD+C,OAAOA,KAAKnC,MAAMoC,UAAU,EAAE,CAACtB,MAAM;CACvC;CACA,OAAO;EAAEtB;EAAOsC;EAAQG;CAAO;AACjC;AAEA,MAAaM,oBACXC,YAGG;CACH,MAAMI,OAAOJ,QAAQK,IAAI3D,cAAc;CACvC,MAAM4D,cAAcN,QAAQK,IAAI5D,eAAe;CAC/C,IAAI2D,SAAS,QAAQE,gBAAgB,MACnC,OAAO1D,QAAQ,iCAAiC;CAClD,MAAM2D,OAAO3B,UAAUwB,IAAI;CAC3B,IAAI,CAACG,KAAKjB,UAAU,CAAC,GAAGiB,KAAKd,OAAOe,KAAK,CAAC,CAAC,CAACC,MAAMC,MAAMA,MAAM,aAAa,GACzE9D,QAAQ,2BAA2B;CACrC,MAAM5B,aAAauF,KAAKd,OAAOY,IAAI,aAAa;CAChD,IAAIrF,eAAe4C,KAAAA,KAAa,OAAO5C,eAAe,UACpD,OAAO4B,QAAQ,8BAA8B;CAC/C,MAAMpB,cAAckC,cAAc;EAChC3C,SAASwF,KAAKvD;EACd,GAAIhC,eAAe4C,KAAAA,IAAY,CAAC,IAAI,EAAE5C,WAAW;CACnD,CAAC;CACD,IAAIsF,YAAYpB,KAAK,MAAM,KACzB,OAAO;EAAE1D;EAAaC,sBAAsB;CAAI;CAClD,MAAMkF,wBAAQ,IAAItC,IAAY;CAqC9B,OAAO;EAAE7C;EAAaC,sBApCO6E,YAC1BM,MAAM,GAAG,CAAC,CACVrC,KAAKU,SAAkC;GACtC,MAAM,EAAEjC,OAAO9B,MAAMoE,QAAQG,WAAWb,UAAUK,IAAI;GACtD,IACEK,UACA,CAAC3C,cAAcM,KAAK/B,IAAI,KACxBA,SAAS,OACTyF,MAAMlC,IAAIvD,IAAI,GAEd0B,QAAQ,oCAAoC;GAC9C+D,MAAMjC,IAAIxD,IAAI;GACd,IACE,CAAC,GAAGuE,OAAOe,KAAK,CAAC,CAAC,CAACC,MAChBC,MAAM,CAAC;IAAC;IAAW;IAAe;GAAU,CAAC,CAACG,SAASH,CAAC,CAC3D,GAEA9D,QAAQ,uBAAuB;GACjC,MAAM7B,UAAU0E,OAAOY,IAAI,SAAS;GACpC,MAAMrF,aAAayE,OAAOY,IAAI,aAAa;GAC3C,MAAMlF,WAAWsE,OAAOY,IAAI,UAAU,KAAK;GAC3C,IACE,OAAOtF,YAAY,YAClBC,eAAe4C,KAAAA,KAAa,OAAO5C,eAAe,YACnD,OAAOG,aAAa,WAEpB,OAAOyB,QAAQ,6BAA6B;GAC9C,OAAO;IACL1B;IACA,GAAGwC,cAAc;KACf3C;KACA,GAAIC,eAAe4C,KAAAA,IAAY,CAAC,IAAI,EAAE5C,WAAW;IACnD,CAAC;IACDG;GACF;EACF,CACoBM;CAAqB;AAC7C;AAEA,MAAaqF,qBACX9D,OACAgB,YACgC;CAChC,IAAI,OAAOhB,UAAU,YAAYA,UAAU,MACzC,OAAOJ,QAAQ,+BAA+B;CAChD,MAAMmE,MAAM/D;CACZ,MAAMjC,UAAUgC,gBAAgBgE,IAAIhG,OAAO;CAC3C,IAAI,CAAC8C,QAAQ9C,SAASiD,QAAQxC,eAAeY,WAAW,GACtDQ,QAAQ,wCAAwC;CAClD,IAAI,CAACoE,MAAMC,QAAQF,IAAI1F,SAAS,GAC9B,OAAOuB,QAAQ,gCAAgC;CACjD,MAAMuB,SAASJ,eAAeC,OAAO;CACrC,MAAMI,uBAAO,IAAIsB,IAAoB;CACrC,MAAMrE,YAAY0F,IAAI1F,UAAUkD,KAAK2C,UAAmB;EACtD,IAAI,OAAOA,UAAU,YAAYA,UAAU,MACzC,OAAOtE,QAAQ,2BAA2B;EAC5C,MAAM2D,OAAOW;EACb,MAAMhG,OAAOqF,KAAKrF;EAClB,IACE,OAAOA,SAAS,YAChB,CAACyB,cAAcM,KAAK/B,IAAI,KACxBA,SAAS,OACTkD,KAAKK,IAAIvD,IAAI,GAEb,OAAO0B,QAAQ,gCAAgC;EACjD,MAAM7B,UAAUgC,gBAAgBwD,KAAKxF,OAAO;EAC5C,IAAIwF,KAAKjF,WAAWsC,KAAAA,KAAa2C,KAAKjF,WAAW,aAC/CsB,QAAQ,kCAAkC;EAC5C,MAAMtB,SAASiF,KAAKjF;EACpB,MAAM6F,SAASjG,KAAKsC,MAAM,GAAGtC,KAAKkG,YAAY,GAAG,CAAC;EAElD,IADcD,WAAW,MAAM/C,KAAKK,IAAI0C,MAAM,GAExC/C;OAAAA,KAAKiC,IAAIc,MAAM,MAAMpG,SACvB6B,QAAQ,6CAA6C;EAAA,OAClD,IAAIuB,WAAW,KAAK;GACzB,MAAMK,QAAQL,OAAOmD,MAAMC,MAAMA,EAAErG,SAASA,IAAI;GAChD,IAAI,CAACsD,SAAS,CAACX,QAAQ9C,SAASyD,KAAK,GACnC5B,QAAQ,oCAAoC;EAChD;EACAwB,KAAK0B,IAAI5E,MAAMH,OAAO;EACtB,OAAO;GAAEG;GAAMH;GAAS,GAAIO,WAAWsC,KAAAA,KAAa,EAAEtC,OAAO;EAAG;CAClE,CAAC;CACD,IAAI6C,WAAW,OAAOA,OAAOsC,MAAMc,MAAM,CAACA,EAAEpG,YAAY,CAACiD,KAAKK,IAAI8C,EAAErG,IAAI,CAAC,GACvE0B,QAAQ,0CAA0C;CACpD,OAAO;EAAE7B;EAASM;CAAU;AAC9B;AAEA,MAAamG,sBACXxB,SACAI,MACAqB,gBAWO;CACP,MAAMM,YAAY;EAChB,GAAGrE,cAAc0C,IAAI;EACrB/E,WAAWoG,YAAYlD,KAAKyD,OAAO;GAAE9G,MAAM8G,EAAE9G;GAAM,GAAGwC,cAAcsE,CAAC;EAAE,EAAE;CAC3E;CACA,MAAMC,WAAWL,MAAc/E,aAAqB,EAClD6E,KAAK;EACHC,QAAQ;EACRC;EACA/E;EACAgF,QAAQD,SAAS,wBAAwB,MAAM;EAC/C,GAAIA,SAAS,wBAAwB,CAAC,IAAI,EAAEE,SAASC,UAAU;CACjE,EACF;CACA,IAAII;CACJ,IAAI;EACFA,UAAUpC,iBAAiBC,OAAO;CACpC,SAASqC,OAAO;EACd,OAAOJ,QACL,uBACAI,iBAAiBvF,QAAQuF,MAAMxF,UAAU,qBAC3C;CACF;CACA,MAAM9B,UAAU,CAACqF,KAAKrF,SAASoH,QAAQ3G,YAAYT,OAAO,CAAC,CAACuH,KAAK,CAAC,CAAC;CACnE,IAAI,CAACzE,QAAQ9C,SAASqF,IAAI,KAAK,CAACvC,QAAQ9C,SAASoH,QAAQ3G,WAAW,GAClE,OAAOyG,QAAQ,uBAAuB,mCAAmC;CAC3E,MAAM9D,SACJgE,QAAQ1G,yBAAyB,MAC7BgG,YAAYlD,KAAKyD,OAAO;EACtB9G,MAAM8G,EAAE9G;EACRH,SAASiH,EAAEjH;EACXI,UAAU;CACZ,EAAE,IACFgH,QAAQ1G;CACd,MAAM8G,WAAW,IAAI7C,IAAI+B,YAAYlD,KAAKyD,MAAM,CAACA,EAAE9G,MAAM8G,CAAC,CAAC,CAAC;CAC5D,IAAIO,SAASC,SAASf,YAAYnD,QAChC,MAAM,IAAIxB,MAAM,0CAA0C;CAC5D,KAAK,MAAM2F,cAAchB,aAAa;EACpC,MAAMiB,2BAAW,IAAIrE,IAAY;EACjC,KAAK,MAAMgD,SAASoB,WAAWzG,gBAAgB,CAAA,GAAI;GACjD,IACE,CAACW,cAAcM,KAAKoE,MAAMnG,IAAI,KAC9BmG,MAAMnG,KAAK2F,SAAS,GAAG,KACvB6B,SAASjE,IAAI4C,MAAMnG,IAAI,KACtBmG,MAAM/F,WAAWsC,KAAAA,KAAayD,MAAM/F,WAAW,aAEhD,MAAM,IAAIwB,MAAM,6CAA6C;GAC/D4F,SAAShE,IAAI2C,MAAMnG,IAAI;EACzB;CACF;CACA,MAAMyH,UAAU,IAAIjD,IAAIvB,OAAOI,KAAKgD,MAAM,CAACA,EAAErG,MAAMqG,CAAC,CAAC,CAAC;CACtD,MAAMqB,SAASjC,UAAuB;EACpC,MAAMkC,SAAS,IAAIxE,IAAIsC,KAAK;EAC5B,KAAK,MAAMzF,QAAQ2H,QAAQ;GACzB,MAAMJ,aAAaF,SAASlC,IAAInF,IAAI;GACpC,IAAI,CAACuH,cAAc,CAACE,QAAQlE,IAAIvD,IAAI,GAAG,OAAO0C,KAAAA;GAC9C,KAAK,MAAMkF,cAAcC,OAAOvC,KAAKiC,WAAW1G,YAAY,CAAC,CAAC,GAC5D8G,OAAOnE,IAAIoE,UAAU;EACzB;EACA,MAAME,yBAAS,IAAItD,IAAoB;EACvC,KAAK,MAAMxE,QAAQ2H,QAAQ;GACzB,MAAMI,SAAS;IACbV,SAASlC,IAAInF,IAAI;IACjByH,QAAQtC,IAAInF,IAAI;IAChB,GAAG,CAAC,GAAG2H,MAAM,CAAC,CAACK,SAAS/B,WAAW;KACjC,MAAM2B,aAAaP,SAASlC,IAAIc,MAAM,CAAC,CAAEpF,WAAWb;KACpD,OAAO4H,aAAa,CAACA,UAAU,IAAI,CAAA;IACrC,CAAC;GAAC,CACH,CAACvE,IAAIb,aAAa;GACnB,MAAMyF,QAAQF,OACX1E,KAAK6E,MAAMA,EAAEpI,UAAU,CAAC,CACxBsH,KAAK,CAAC,CACNe,GAAG,EAAE;GACR,MAAMC,QAAQL,OAAO1E,KAAK6E,MAAMA,EAAErI,OAAO,CAAC,CAACuH,KAAK,CAAC,CAAC;GAClD,IAAIa,QAAQG,OAAO,OAAO1F,KAAAA;GAC1BoF,OAAOlD,IAAI5E,MAAMoI,KAAK;EACxB;EACA,OAAON;CACT;CACA,IAAIO,WAAWX,MACb,IAAIvE,IAAIF,OAAOqF,QAAQjC,MAAM,CAACA,EAAEpG,QAAQ,CAAC,CAACoD,KAAKgD,MAAMA,EAAErG,IAAI,CAAC,CAC9D;CACA,IAAI,CAACqI,UACH,OAAOtB,QACL,wBACA,8CACF;CACF,KAAK,MAAMzD,SAASL,QAClB,IAAIK,MAAMrD,UACRoI,WAAWX,sBAAM,IAAIvE,IAAI,CAAC,GAAGkF,SAAS/C,KAAK,GAAGhC,MAAMtD,IAAI,CAAC,CAAC,KAAKqI;CAEnE,OAAO;EACLxI;EACAM,WAAW8C,OAAO+E,SAAS3B,MAAM;GAC/B,MAAMxG,UAAUwI,SAAUlD,IAAIkB,EAAErG,IAAI;GACpC,IAAIH,YAAY6C,KAAAA,GAAW,OAAO,CAAA;GAClC,OAAO,CACL;IAAE1C,MAAMqG,EAAErG;IAAMH;GAAQ,GACxB,IAAIwH,SAASlC,IAAIkB,EAAErG,IAAI,CAAC,CAAEc,gBAAgB,CAAA,EAAA,CAAIuC,KAAK8C,WAAW;IAC5DnG,MAAM,GAAGqG,EAAErG,KAAI,GAAImG,MAAMnG;IACzBH;IACA,GAAIsG,MAAM/F,WAAWsC,KAAAA,KAAa,EAAEtC,QAAQ+F,MAAM/F,OAAO;GAC3D,EAAE,CAAC;EAEP,CAAC;CACH;AACF"}
|
package/package.json
CHANGED
package/src/client/connection.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
ATTACHER_HEADER,
|
|
2
3
|
negotiationHeaders,
|
|
3
4
|
validateSelection,
|
|
4
5
|
parseNegotiation,
|
|
@@ -9,6 +10,7 @@ import { flushTapSync } from "@assistant-ui/tap";
|
|
|
9
10
|
import { reviveOps, reviveValue } from "../wire/meta";
|
|
10
11
|
import { backoff, retryHint, timing } from "./timing";
|
|
11
12
|
import { isStorageError } from "./storageError";
|
|
13
|
+
import { randomId } from "../util/randomId";
|
|
12
14
|
import type {
|
|
13
15
|
ConnectionState,
|
|
14
16
|
RemoteLink,
|
|
@@ -111,6 +113,8 @@ export type Conn = {
|
|
|
111
113
|
connection: StatewireClient.Connection;
|
|
112
114
|
parked: boolean;
|
|
113
115
|
lease: string | null;
|
|
116
|
+
/** Names every attach of this connection, so a host retires the earlier ones (2026-11-02). */
|
|
117
|
+
attacher: string;
|
|
114
118
|
attempt: number;
|
|
115
119
|
authDenials: number;
|
|
116
120
|
authStale: boolean;
|
|
@@ -642,6 +646,11 @@ export const resolveHeaders = async (
|
|
|
642
646
|
);
|
|
643
647
|
|
|
644
648
|
// The stale flag clears only once the resolver returns: a throwing refresh keeps it for the next attempt.
|
|
649
|
+
/** Names this connection, so a host retires its earlier attaches before the hello. */
|
|
650
|
+
export const replacing = (c: Conn, headers: Headers) => {
|
|
651
|
+
headers.set(ATTACHER_HEADER, c.attacher);
|
|
652
|
+
};
|
|
653
|
+
|
|
645
654
|
export const attachFrame = async (
|
|
646
655
|
c: Conn,
|
|
647
656
|
headers: StatewireClient.HeadersOption | undefined,
|
|
@@ -654,6 +663,7 @@ export const attachFrame = async (
|
|
|
654
663
|
)) {
|
|
655
664
|
if (!resolved.has(name)) resolved.set(name, value);
|
|
656
665
|
}
|
|
666
|
+
replacing(c, resolved);
|
|
657
667
|
c.negotiation = parseNegotiation(resolved);
|
|
658
668
|
c.authStale = false;
|
|
659
669
|
const persistence = c.ctx.beforeAttach?.();
|
|
@@ -686,6 +696,7 @@ export const useLink = (
|
|
|
686
696
|
const conn: Conn = {
|
|
687
697
|
parked: props.isNew ?? false,
|
|
688
698
|
lease: null,
|
|
699
|
+
attacher: randomId(),
|
|
689
700
|
attempt: 0,
|
|
690
701
|
authDenials: 0,
|
|
691
702
|
authStale: false,
|
package/src/client/session.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { encodeContext } from "../wire/context";
|
|
|
2
2
|
import { wrapStorageError } from "./storageError";
|
|
3
3
|
import {
|
|
4
4
|
DEFAULT_PROTOCOL,
|
|
5
|
+
CLIENT_WIRE,
|
|
5
6
|
GAP_FILL_VERSION,
|
|
6
|
-
WIRE_VERSION,
|
|
7
7
|
protocolOffers,
|
|
8
8
|
inRange,
|
|
9
9
|
} from "../wire/protocol";
|
|
@@ -163,6 +163,8 @@ type Session = {
|
|
|
163
163
|
/** Ephemeral lane clocks: restart at 1 on every attach. */
|
|
164
164
|
ephemeralSeqs: Record<string, number>;
|
|
165
165
|
resumeAt: number;
|
|
166
|
+
/** A refusal named sent durable stamps: a reattach's hello decides them, after `resumeAt`. */
|
|
167
|
+
reattach: boolean;
|
|
166
168
|
idle: boolean;
|
|
167
169
|
onSessionLost: StatewireClient.OnSessionLost | undefined;
|
|
168
170
|
onDeprecatedPath: StatewireClient.OnDeprecatedPath | undefined;
|
|
@@ -1326,14 +1328,16 @@ const sessionLostInner = (s: Session, protocol: string, seqWm: number) => {
|
|
|
1326
1328
|
},
|
|
1327
1329
|
);
|
|
1328
1330
|
} else {
|
|
1329
|
-
// Default: the
|
|
1331
|
+
// Default: the queue goes, each command a failure notice. Held entries
|
|
1332
|
+
// never went out: they stay held where they are.
|
|
1330
1333
|
const stamps = mergeBacklog(s, protocol);
|
|
1331
|
-
const
|
|
1332
|
-
const
|
|
1334
|
+
const held = outboxOf(lane).filter((c) => c.held);
|
|
1335
|
+
const queued = outboxOf(lane).filter((c) => !c.held);
|
|
1336
|
+
const keep = new Set(held.map((c) => c.id));
|
|
1333
1337
|
setRecord(
|
|
1334
1338
|
s,
|
|
1335
1339
|
withLane(s.record, protocol, {
|
|
1336
|
-
...withNotices(
|
|
1340
|
+
...withNotices(withOutbox(lane, held), [
|
|
1337
1341
|
...stamps.map((c) => lostNotice(c)),
|
|
1338
1342
|
...queued.map((c) => lostNotice(c)),
|
|
1339
1343
|
]),
|
|
@@ -1350,7 +1354,9 @@ const sessionLostInner = (s: Session, protocol: string, seqWm: number) => {
|
|
|
1350
1354
|
),
|
|
1351
1355
|
}),
|
|
1352
1356
|
);
|
|
1353
|
-
for (const p of s.pending.filter(
|
|
1357
|
+
for (const p of s.pending.filter(
|
|
1358
|
+
(q) => q.command.protocol === protocol && !keep.has(q.id),
|
|
1359
|
+
)) {
|
|
1354
1360
|
s.lifecycle?.onCovered?.(p.inv);
|
|
1355
1361
|
settleEntry(s, p, { ok: false, failure }, "noticed");
|
|
1356
1362
|
}
|
|
@@ -1451,6 +1457,21 @@ const selectionOf = (
|
|
|
1451
1457
|
};
|
|
1452
1458
|
};
|
|
1453
1459
|
|
|
1460
|
+
// The host admits an attach's durable statements right after its hello.
|
|
1461
|
+
const carriedThrough = (
|
|
1462
|
+
lane: { name: string; seq?: number },
|
|
1463
|
+
attached: ReadonlySet<Pending>,
|
|
1464
|
+
) => {
|
|
1465
|
+
const carried = new Set(
|
|
1466
|
+
[...attached]
|
|
1467
|
+
.filter((p) => p.command.protocol === lane.name)
|
|
1468
|
+
.map((p) => p.seq),
|
|
1469
|
+
);
|
|
1470
|
+
let seq = lane.seq ?? 0;
|
|
1471
|
+
while (carried.has(seq + 1)) seq += 1;
|
|
1472
|
+
return seq;
|
|
1473
|
+
};
|
|
1474
|
+
|
|
1454
1475
|
const handleHello = (
|
|
1455
1476
|
s: Session,
|
|
1456
1477
|
{ ops, syn }: { ops: unknown[]; syn: StatewireSyn },
|
|
@@ -1472,6 +1493,7 @@ const handleHello = (
|
|
|
1472
1493
|
s.attachEpoch += 1;
|
|
1473
1494
|
s.frameFailures = 0;
|
|
1474
1495
|
s.resumeAt = 0;
|
|
1496
|
+
s.reattach = false;
|
|
1475
1497
|
const lanes = syn.protocols ?? [];
|
|
1476
1498
|
s.ephemeral =
|
|
1477
1499
|
lanes.length > 0 && lanes.every((lane) => lane.record === "ephemeral");
|
|
@@ -1494,7 +1516,7 @@ const handleHello = (
|
|
|
1494
1516
|
);
|
|
1495
1517
|
continue;
|
|
1496
1518
|
}
|
|
1497
|
-
demote(s, lane.name, lane
|
|
1519
|
+
demote(s, lane.name, carriedThrough(lane, attached), demoted, waiting);
|
|
1498
1520
|
s.durableSeq[lane.name] = lane.seq ?? 0;
|
|
1499
1521
|
}
|
|
1500
1522
|
for (const entry of s.pending) {
|
|
@@ -1543,10 +1565,26 @@ const handleOps = (
|
|
|
1543
1565
|
// A retry-less {syn} refusal is the one session-loss verdict: a gap against
|
|
1544
1566
|
// a durable lane's exact watermark settles that lane's history lost; a
|
|
1545
1567
|
// pushback (retry present) or a gapless refusal only reconciles the resend queues.
|
|
1568
|
+
// Under the gap-fill wire a refusal can predate frames sent since: durable
|
|
1569
|
+
// lanes never resend from it, a reattach's hello decides them.
|
|
1546
1570
|
const handleRefusal = (s: Session, syn: StatewireSyn) => {
|
|
1547
|
-
const
|
|
1571
|
+
const stated = (syn.protocols ?? []).filter(
|
|
1548
1572
|
(lane): lane is { name: string; seq: number } => lane.seq !== undefined,
|
|
1549
1573
|
);
|
|
1574
|
+
const lanes = gapFill(s)
|
|
1575
|
+
? stated.filter(({ name }) => isEphemeral(s, name))
|
|
1576
|
+
: stated;
|
|
1577
|
+
if (
|
|
1578
|
+
gapFill(s) &&
|
|
1579
|
+
s.pending.some((p) => {
|
|
1580
|
+
if (!isStamped(p) || !p.attempted) return false;
|
|
1581
|
+
const protocol = p.command.protocol;
|
|
1582
|
+
if (isEphemeral(s, protocol)) return false;
|
|
1583
|
+
const seq = stated.find((lane) => lane.name === protocol)?.seq ?? 0;
|
|
1584
|
+
return p.seq > Math.max(seq, durableSeqOf(s, protocol));
|
|
1585
|
+
})
|
|
1586
|
+
)
|
|
1587
|
+
s.reattach = true;
|
|
1550
1588
|
for (const { name, seq } of lanes) {
|
|
1551
1589
|
const watermark = isEphemeral(s, name)
|
|
1552
1590
|
? seq
|
|
@@ -1728,6 +1766,29 @@ const pump = async (s: Session) => {
|
|
|
1728
1766
|
while (s.running && !isFinished(s)) {
|
|
1729
1767
|
if (!s.hydrated) await s.ready;
|
|
1730
1768
|
const transport = s.transport;
|
|
1769
|
+
if (s.reattach && s.attach === "attached" && transport) {
|
|
1770
|
+
const delay = s.resumeAt - Date.now();
|
|
1771
|
+
if (delay > 0) {
|
|
1772
|
+
await sleepUntilWake(s, delay);
|
|
1773
|
+
continue;
|
|
1774
|
+
}
|
|
1775
|
+
// Ephemeral answers ride only this attach: drain them first.
|
|
1776
|
+
if (
|
|
1777
|
+
s.pending.some(
|
|
1778
|
+
(p) =>
|
|
1779
|
+
isStamped(p) && p.attempted && isEphemeral(s, p.command.protocol),
|
|
1780
|
+
)
|
|
1781
|
+
) {
|
|
1782
|
+
await waitForWake(s);
|
|
1783
|
+
continue;
|
|
1784
|
+
}
|
|
1785
|
+
s.reattach = false;
|
|
1786
|
+
const epoch = s.attachEpoch;
|
|
1787
|
+
transport.connection.reconnect();
|
|
1788
|
+
while (s.running && !isFinished(s) && epoch === s.attachEpoch)
|
|
1789
|
+
await waitForWake(s);
|
|
1790
|
+
continue;
|
|
1791
|
+
}
|
|
1731
1792
|
const flushable = flushCandidates(s).length > 0;
|
|
1732
1793
|
const queued =
|
|
1733
1794
|
s.pending.some((p) => isOutboxed(p) && !p.inv.held) ||
|
|
@@ -2444,7 +2505,7 @@ export const updateOutbox = (
|
|
|
2444
2505
|
const producedOf = (
|
|
2445
2506
|
s: Session,
|
|
2446
2507
|
): NonNullable<StatewireClient.SessionCommand["produced"]> => ({
|
|
2447
|
-
wire: s.options.wireVersion ??
|
|
2508
|
+
wire: s.options.wireVersion ?? CLIENT_WIRE,
|
|
2448
2509
|
protocols:
|
|
2449
2510
|
protocolOffers(s.options) === "*" && s.selection
|
|
2450
2511
|
? s.selection.protocols
|
|
@@ -2512,6 +2573,7 @@ export const insertOutbox = (
|
|
|
2512
2573
|
return;
|
|
2513
2574
|
}
|
|
2514
2575
|
let base = s.record;
|
|
2576
|
+
let entered = stored;
|
|
2515
2577
|
if (entry.from !== undefined) {
|
|
2516
2578
|
// Re-inserting a settled update consumes it in the same write: a
|
|
2517
2579
|
// redelivery finds it gone.
|
|
@@ -2519,16 +2581,27 @@ export const insertOutbox = (
|
|
|
2519
2581
|
l.inbox?.some((n) => n.id === entry.from),
|
|
2520
2582
|
);
|
|
2521
2583
|
if (source === undefined || locate(s, id) !== undefined) return;
|
|
2584
|
+
const notice = source[1].inbox!.find((n) => n.id === entry.from)!;
|
|
2585
|
+
entered = { ...stored, ts: notice.ts };
|
|
2522
2586
|
base = withLane(base, source[0], withoutNotice(source[1], entry.from));
|
|
2523
2587
|
}
|
|
2524
2588
|
const lane = laneOf(base, protocol);
|
|
2589
|
+
// A re-queued command keeps its place: ahead of what was queued after it.
|
|
2590
|
+
const later = outboxOf(lane).find((c) => c.ts >= entered.ts);
|
|
2591
|
+
const position =
|
|
2592
|
+
entry.from !== undefined &&
|
|
2593
|
+
entry.after === undefined &&
|
|
2594
|
+
entry.before === undefined &&
|
|
2595
|
+
later !== undefined
|
|
2596
|
+
? { before: later.id }
|
|
2597
|
+
: entry;
|
|
2525
2598
|
try {
|
|
2526
2599
|
setRecord(
|
|
2527
2600
|
s,
|
|
2528
2601
|
withLane(
|
|
2529
2602
|
base,
|
|
2530
2603
|
protocol,
|
|
2531
|
-
withOutbox(lane, placed(outboxOf(lane),
|
|
2604
|
+
withOutbox(lane, placed(outboxOf(lane), entered, position)),
|
|
2532
2605
|
),
|
|
2533
2606
|
);
|
|
2534
2607
|
} catch (error) {
|
|
@@ -2800,7 +2873,7 @@ const loadSessionRecord = (
|
|
|
2800
2873
|
let stored = store.load();
|
|
2801
2874
|
if (stored !== null) validateLanes(stored);
|
|
2802
2875
|
const current = {
|
|
2803
|
-
wire: options.wireVersion ??
|
|
2876
|
+
wire: options.wireVersion ?? CLIENT_WIRE,
|
|
2804
2877
|
protocols: protocolOffers(options),
|
|
2805
2878
|
};
|
|
2806
2879
|
const compatible = (record: StatewireClient.SessionRecord) =>
|
|
@@ -2933,6 +3006,7 @@ export const useStatewireSession = (
|
|
|
2933
3006
|
durableSeq: {},
|
|
2934
3007
|
ephemeralSeqs: {},
|
|
2935
3008
|
resumeAt: 0,
|
|
3009
|
+
reattach: false,
|
|
2936
3010
|
idle: false,
|
|
2937
3011
|
onSessionLost: undefined,
|
|
2938
3012
|
onDeprecatedPath: undefined,
|