statewire 0.9.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/client/connection.d.ts +5 -2
  2. package/dist/client/connection.d.ts.map +1 -1
  3. package/dist/client/connection.js +123 -23
  4. package/dist/client/connection.js.map +1 -1
  5. package/dist/client/session.d.ts +7 -4
  6. package/dist/client/session.d.ts.map +1 -1
  7. package/dist/client/session.js +62 -15
  8. package/dist/client/session.js.map +1 -1
  9. package/dist/client/sessionStore.d.ts +5 -14
  10. package/dist/client/sessionStore.d.ts.map +1 -1
  11. package/dist/client/sessionStore.js +4 -1
  12. package/dist/client/sessionStore.js.map +1 -1
  13. package/dist/client/timing.d.ts +2 -0
  14. package/dist/client/timing.d.ts.map +1 -1
  15. package/dist/client/timing.js +8 -2
  16. package/dist/client/timing.js.map +1 -1
  17. package/dist/host/http.d.ts +7 -0
  18. package/dist/host/http.d.ts.map +1 -0
  19. package/dist/host/http.js +175 -0
  20. package/dist/host/http.js.map +1 -0
  21. package/dist/host/socket.d.ts +102 -1
  22. package/dist/host/socket.d.ts.map +1 -1
  23. package/dist/host/socket.js +48 -27
  24. package/dist/host/socket.js.map +1 -1
  25. package/dist/host-internal.d.ts +5 -0
  26. package/dist/host-internal.d.ts.map +1 -1
  27. package/dist/host.d.ts +1 -1
  28. package/dist/host.d.ts.map +1 -1
  29. package/dist/statewire.d.ts +34 -8
  30. package/dist/statewire.d.ts.map +1 -1
  31. package/dist/statewire.js +9 -2
  32. package/dist/statewire.js.map +1 -1
  33. package/dist/transports/sse.d.ts.map +1 -1
  34. package/dist/transports/sse.js +6 -4
  35. package/dist/transports/sse.js.map +1 -1
  36. package/dist/transports/ws.d.ts.map +1 -1
  37. package/dist/transports/ws.js +5 -9
  38. package/dist/transports/ws.js.map +1 -1
  39. package/dist/types.d.ts +1 -1
  40. package/dist/types.js.map +1 -1
  41. package/package.json +1 -1
  42. package/src/client/connection.ts +212 -40
  43. package/src/client/session.ts +90 -24
  44. package/src/client/sessionStore.ts +8 -15
  45. package/src/client/timing.ts +10 -2
  46. package/src/host/http.ts +280 -0
  47. package/src/host/socket.ts +120 -57
  48. package/src/host-internal.ts +5 -0
  49. package/src/host.ts +1 -0
  50. package/src/statewire.ts +65 -9
  51. package/src/transports/sse.ts +6 -5
  52. package/src/transports/ws.ts +4 -9
  53. package/src/types.ts +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","names":["useEffect","useState","applyOps","StatewirePath","StatewireCmd","StatewireSyn","encodeParams","encodeValue","reviveResponse","bindCommandsHost","CommandsInvocation","CommandsListener","Statewire","StatewireClient","createMemorySessionStore","StatewireSessionRecord","StatewireSessionStore","StatewireSessionStoreOptions","backoff","createMethodProxy","randomId","StatewireSendError","RemoteLink","StatewireFrame","StatewireRemoteProps","WireCommand","method","params","meta","Pending","seq","command","inv","attempted","posted","applied","resolve","payload","reject","error","CtxPending","ops","protocolError","message","Error","newClientId","rejectedSend","failure","SendFailure","Promise","result","catch","AttachState","StatewireSessionOptions","onDone","Session","record","ctx","recordPersisted","store","ephemeral","sessionKnown","durableSeq","resumeAt","idle","ctxOps","ctxPending","lanesOnAttach","attachLaneSeqs","ReadonlySet","frameFailures","pending","canonical","lastPublished","changed","source","StateSource","attach","attachEpoch","running","pumping","transport","waiters","lifecycle","notify","commands","Record","publish","s","wake","waiter","waitForWake","r","push","sleepUntilWake","delayMs","clearTimeout","timer","setTimeout","filter","w","setRecord","save","setIdle","level","cover","entry","onApplied","onCovered","settleEntry","outcome","ok","p","onSettle","type","settleAll","detach","cause","unmount","gone","superseded","const","batch","detachFailure","predecessorLost","some","fate","restateContext","undefined","ctxNextSeq","resetRecord","clientId","survivors","nextSeq","old","enqueue","ephemeralDetach","rejectedFailure","cmd","code","handleCmd","dur","Math","max","find","String","handleCmds","cmds","ingestOps","state","changedPaths","publishIngested","RECORDS","snapClocks","seqWm","ctxWm","tail","cmdAligned","every","i","length","ctxAligned","c","handleHello","syn","includes","attachCmd","attachCtx","Set","has","ctxWatermark","ctxSeq","pump","handleOps","handleRefusal","watermark","retry","Date","now","firstCmd","firstCtx","clocksAhead","connection","reconnect","handleDetach","handleFinish","reason","isFinished","Lanes","frame","cmdEntries","ctxEntries","buildLanes","at","map","takeAttachLanes","wanted","lanes","status","resumeDelay","epoch","send","onPosted","start","stop","setTransport","ts","onStart","sendCommand","encoded","setContext","value","Array","isArray","op","path","useStatewireSession","options","defaultStore","sessionStore","stored","load","session","onHello","args","onOps","onCmds","onRefusal","onIdle","onDetach","onFinish","snapshot","takeChanged","setOnChange","listener","setListener"],"sources":["../../src/client/session.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { applyOps } from \"../wire/ops\";\nimport type { StatewirePath } from \"../wire/ops\";\nimport type { StatewireCmd, StatewireSyn } from \"./connection\";\nimport { encodeParams, encodeValue, reviveResponse } from \"../wire/meta\";\nimport { bindCommandsHost } from \"../host/host\";\nimport type { CommandsInvocation, CommandsListener } from \"../host/host\";\nimport type { Statewire } from \"../types\";\nimport type { StatewireClient } from \"../statewire\";\nimport { createMemorySessionStore } from \"./sessionStore\";\nimport type {\n StatewireSessionRecord,\n StatewireSessionStore,\n StatewireSessionStoreOptions,\n} from \"./sessionStore\";\nimport { backoff } from \"./timing\";\nimport { createMethodProxy } from \"../util/methodProxy\";\nimport { randomId } from \"../util/randomId\";\nimport { StatewireSendError } from \"../types\";\nimport type {\n RemoteLink,\n StatewireFrame,\n StatewireRemoteProps,\n} from \"../transports/sse\";\n\ntype WireCommand = {\n method: string;\n params: unknown[];\n meta?: unknown;\n};\n\ntype Pending = {\n readonly seq: number;\n readonly command: WireCommand;\n readonly inv: CommandsInvocation;\n /** A send carrying this statement may have reached the server. */\n attempted: boolean;\n /** In the resend queue when false. */\n posted: boolean;\n /** The server acknowledged admission; the terminal verdict is still due. */\n applied: boolean;\n resolve: (payload: unknown) => void;\n reject: (error: StatewireSendError) => void;\n};\n\ntype CtxPending = {\n readonly seq: number;\n readonly ops: unknown[];\n posted: boolean;\n};\n\nconst protocolError = (message: string): never => {\n throw new Error(`statewire: protocol error: ${message}`);\n};\n\nconst newClientId = () => `c-${randomId()}`;\n\nconst rejectedSend = (\n failure: StatewireClient.SendFailure,\n): Promise<unknown> => {\n const result = Promise.reject<unknown>(new StatewireSendError(failure));\n result.catch(() => {});\n return result;\n};\n\ntype AttachState = \"detached\" | \"attached\" | \"finished\";\n\nexport type StatewireSessionOptions = StatewireSessionStoreOptions & {\n onDone?: (() => void) | undefined;\n};\n\ntype Session = {\n record: StatewireSessionRecord;\n ctx: StatewireRemoteProps;\n recordPersisted: boolean;\n store: StatewireSessionStore;\n ephemeral: boolean;\n sessionKnown: boolean;\n durableSeq: number;\n resumeAt: number;\n idle: boolean;\n onDone: (() => void) | undefined;\n ctxOps: unknown[] | undefined;\n ctxPending: CtxPending[];\n lanesOnAttach: boolean;\n attachLaneSeqs: readonly [ReadonlySet<number>, ReadonlySet<number>];\n frameFailures: number;\n pending: Pending[];\n canonical: unknown;\n lastPublished: unknown;\n changed: StatewirePath[];\n source: Statewire.StateSource;\n attach: AttachState;\n attachEpoch: number;\n running: boolean;\n pumping: boolean;\n transport: RemoteLink | null;\n waiters: (() => void)[];\n lifecycle: CommandsListener | null;\n notify: () => void;\n commands: Record<string, (...params: unknown[]) => Promise<unknown>>;\n};\n\nconst publish = (s: Session) => {\n if (s.canonical === s.lastPublished) return;\n s.lastPublished = s.canonical;\n s.notify();\n};\n\nconst wake = (s: Session) => {\n const waiters = s.waiters;\n s.waiters = [];\n for (const waiter of waiters) waiter();\n};\n\nconst waitForWake = (s: Session) => new Promise<void>((r) => s.waiters.push(r));\n\nconst sleepUntilWake = (s: Session, delayMs: number) =>\n new Promise<void>((resolve) => {\n const waiter = () => {\n clearTimeout(timer);\n resolve();\n };\n const timer = setTimeout(() => {\n s.waiters = s.waiters.filter((w) => w !== waiter);\n resolve();\n }, delayMs);\n s.waiters.push(waiter);\n });\n\nconst setRecord = (s: Session, record: StatewireSessionRecord) => {\n s.record = record;\n if (!s.ephemeral) s.store.save(record);\n};\n\nconst setIdle = (s: Session, level: boolean) => {\n if (level === s.idle) return;\n s.idle = level;\n s.notify();\n if (level) s.onDone?.();\n};\n\nconst cover = (s: Session, entry: Pending) => {\n if (entry.applied) return;\n entry.applied = true;\n s.lifecycle?.onApplied(entry.inv);\n s.lifecycle?.onCovered?.(entry.inv);\n};\n\nconst settleEntry = (\n s: Session,\n entry: Pending,\n outcome:\n | { ok: true; payload: unknown }\n | { ok: false; failure: StatewireClient.SendFailure },\n) => {\n s.pending = s.pending.filter((p) => p !== entry);\n if (outcome.ok) {\n entry.resolve(outcome.payload);\n s.lifecycle?.onSettle(entry.inv, {\n type: \"accepted\",\n payload: outcome.payload,\n });\n } else {\n entry.reject(new StatewireSendError(outcome.failure));\n s.lifecycle?.onSettle(entry.inv, {\n type: \"rejected\",\n error: new StatewireSendError(outcome.failure),\n });\n }\n};\n\nconst settleAll = (s: Session, detach: \"unmount\" | \"gone\" | \"superseded\") => {\n const cause = (\n {\n unmount: \"unmount\",\n gone: \"stream-gone\",\n superseded: \"superseded\",\n } as const\n )[detach];\n const batch = s.pending;\n const detachFailure = (entry: Pending): StatewireClient.SendFailure => {\n if (!entry.attempted) {\n const predecessorLost = batch.some(\n (p) => p.seq < entry.seq && p.attempted,\n );\n return predecessorLost\n ? { fate: \"aborted\", cause, predecessorLost }\n : { fate: \"aborted\", cause };\n }\n return { fate: \"lost\", cause };\n };\n for (const entry of [...batch]) {\n s.lifecycle?.onCovered?.(entry.inv);\n settleEntry(s, entry, { ok: false, failure: detachFailure(entry) });\n }\n};\n\nconst restateContext = (s: Session) => {\n s.ctxPending = [];\n if (s.ctxOps === undefined) return;\n const seq = s.record.ctxNextSeq;\n setRecord(s, { ...s.record, ctxNextSeq: seq + 1 });\n s.ctxPending.push({ seq, ops: s.ctxOps, posted: false });\n};\n\n// The server-side record's dedup memory is gone: history that may have\n// reached a server settles lost — the new hello's snapshot is the only\n// truth about what survived — and never-attempted statements re-stamp\n// from 1 on the reset lane clocks.\nconst resetRecord = (s: Session, clientId: string) => {\n const survivors: Pending[] = [];\n for (const entry of [...s.pending]) {\n if (entry.attempted || entry.applied) {\n s.lifecycle?.onCovered?.(entry.inv);\n settleEntry(s, entry, {\n ok: false,\n failure: { fate: \"lost\", cause: \"session-lost\" },\n });\n } else {\n survivors.push(entry);\n }\n }\n s.pending = [];\n s.durableSeq = 0;\n setRecord(s, { clientId, nextSeq: 1, ctxNextSeq: 1 });\n for (const old of survivors) {\n s.lifecycle?.onCovered?.(old.inv);\n enqueue(s, old.command, old.inv.params, old.resolve, old.reject);\n }\n restateContext(s);\n publish(s);\n wake(s);\n};\n\n// The record died with the attach: mint a fresh id for the next one.\nconst ephemeralDetach = (s: Session) => {\n s.ephemeral = false;\n s.sessionKnown = false;\n resetRecord(s, newClientId());\n};\n\nconst rejectedFailure = (cmd: StatewireCmd): StatewireClient.SendFailure => {\n const code =\n cmd.code === undefined ||\n cmd.code === \"unknown-command\" ||\n cmd.code === \"invalid-params\"\n ? cmd.code\n : protocolError(`unknown rejected code: ${cmd.code}`);\n return {\n fate: \"rejected\",\n cause: \"server\",\n ...(code !== undefined && { code }),\n ...(cmd.message !== undefined && { message: cmd.message }),\n ...(cmd.payload !== undefined && { payload: cmd.payload }),\n };\n};\n\nconst handleCmd = (s: Session, cmd: StatewireCmd) => {\n if (typeof cmd.seq !== \"number\") protocolError(\"cmd entry missing seq\");\n if (cmd.dur === true) {\n s.durableSeq = Math.max(s.durableSeq, cmd.seq);\n for (const entry of s.pending) {\n if (entry.seq <= s.durableSeq) entry.posted = true;\n }\n }\n if (cmd.type === undefined) return;\n const entry = s.pending.find((p) => p.seq === cmd.seq);\n if (cmd.type === \"applied\") {\n if (entry) cover(s, entry);\n return;\n }\n if (!entry) return;\n cover(s, entry);\n const outcome =\n cmd.type === \"result\"\n ? { ok: true as const, payload: reviveResponse(cmd).payload }\n : cmd.type === \"rejected\"\n ? { ok: false as const, failure: rejectedFailure(reviveResponse(cmd)) }\n : cmd.type === \"failed\"\n ? {\n ok: false as const,\n failure: {\n fate: \"failed\" as const,\n cause: \"server\" as const,\n ...(cmd.message !== undefined && { message: cmd.message }),\n },\n }\n : cmd.type === \"crashed\"\n ? {\n ok: false as const,\n failure: { fate: \"crashed\" as const, cause: \"server\" as const },\n }\n : cmd.type === \"expired\"\n ? {\n ok: false as const,\n failure: {\n fate: \"expired\" as const,\n cause: \"server\" as const,\n },\n }\n : protocolError(`unknown cmd type: ${String(cmd.type)}`);\n settleEntry(s, entry, outcome);\n};\n\nconst handleCmds = (s: Session, cmds: StatewireCmd[]) => {\n for (const cmd of cmds) handleCmd(s, cmd);\n wake(s);\n};\n\nconst ingestOps = (s: Session, ops: unknown[]) => {\n const applied = applyOps(s.canonical, ops);\n s.canonical = applied.state;\n s.changed.push(...applied.changedPaths);\n};\n\nconst publishIngested = (s: Session) => {\n publish(s);\n wake(s);\n};\n\nconst RECORDS = [\"fresh\", \"resumed\", \"ephemeral\"];\n\n// Snap a resumed session's lane clocks onto the record: seqs above the stated\n// watermark were never admitted, so a stale restored clock re-stamps its\n// never-admitted statements consecutively after the watermark.\nconst snapClocks = (s: Session, seqWm: number, ctxWm: number) => {\n const tail = s.pending.filter((p) => p.seq > seqWm);\n const cmdAligned =\n tail.every((p, i) => p.seq === seqWm + 1 + i) &&\n s.record.nextSeq === seqWm + tail.length + 1;\n if (!cmdAligned) {\n s.pending = s.pending.filter((p) => p.seq <= seqWm);\n setRecord(s, { ...s.record, nextSeq: seqWm + 1 });\n for (const old of tail) {\n s.lifecycle?.onCovered?.(old.inv);\n enqueue(s, old.command, old.inv.params, old.resolve, old.reject);\n }\n }\n const ctxAligned =\n s.ctxPending.every((c, i) => c.seq === ctxWm + 1 + i) &&\n s.record.ctxNextSeq === ctxWm + s.ctxPending.length + 1;\n if (!ctxAligned) {\n setRecord(s, { ...s.record, ctxNextSeq: ctxWm + 1 });\n restateContext(s);\n }\n};\n\nconst handleHello = (\n s: Session,\n { ops, syn }: { ops: unknown[]; syn: StatewireSyn },\n) => {\n const record = syn.record ?? \"fresh\";\n if (!RECORDS.includes(record)) {\n protocolError(`unknown record: ${String(record)}`);\n }\n ingestOps(s, ops);\n s.attach = \"attached\";\n s.attachEpoch += 1;\n s.frameFailures = 0;\n s.resumeAt = 0;\n s.ephemeral = record === \"ephemeral\";\n const [attachCmd, attachCtx] = s.attachLaneSeqs;\n s.attachLaneSeqs = [new Set(), new Set()];\n if (record === \"resumed\") {\n // Resending unsettled statements is exactly-once-safe: absorbed replays\n // re-emit their stored verdicts (applied while still in flight).\n for (const entry of s.pending) {\n entry.posted = attachCmd.has(entry.seq);\n }\n const ctxWatermark = syn.ctxSeq ?? 0;\n s.ctxPending = s.ctxPending.filter((c) => c.seq > ctxWatermark);\n for (const c of s.ctxPending) c.posted = attachCtx.has(c.seq);\n snapClocks(s, syn.seq ?? 0, ctxWatermark);\n } else {\n resetRecord(s, s.record.clientId);\n }\n s.sessionKnown = true;\n publishIngested(s);\n void pump(s);\n};\n\nconst handleOps = (\n s: Session,\n { ops, cmds }: { ops: unknown[]; cmds: StatewireCmd[] },\n) => {\n ingestOps(s, ops);\n for (const cmd of cmds) handleCmd(s, cmd);\n publishIngested(s);\n};\n\n// A {syn} refusal never settles: reconcile the resend queues against the\n// stated watermarks; retry present means wait, absent means resend now.\nconst handleRefusal = (s: Session, syn: StatewireSyn) => {\n const { seq, ctxSeq } = syn;\n if (typeof seq === \"number\") {\n const watermark = Math.max(seq, s.durableSeq);\n for (const entry of s.pending) entry.posted = entry.seq <= watermark;\n }\n if (typeof ctxSeq === \"number\") {\n s.ctxPending = s.ctxPending.filter((c) => c.seq > ctxSeq);\n for (const c of s.ctxPending) c.posted = false;\n }\n if (typeof syn.retry === \"number\") {\n s.resumeAt = Date.now() + Math.max(0, syn.retry);\n }\n const firstCmd = s.pending.find((p) => !p.posted);\n const firstCtx = s.ctxPending.find((c) => !c.posted);\n const clocksAhead =\n (typeof syn.seq === \"number\" &&\n firstCmd !== undefined &&\n firstCmd.seq > Math.max(syn.seq, s.durableSeq) + 1) ||\n (typeof syn.ctxSeq === \"number\" &&\n firstCtx !== undefined &&\n firstCtx.seq > syn.ctxSeq + 1);\n // Clocks ahead of a record that cannot admit them: reattach first and let\n // the hello's record arbitrate.\n if (clocksAhead) s.transport?.connection.reconnect();\n wake(s);\n void pump(s);\n};\n\nconst handleDetach = (s: Session) => {\n if (s.attach === \"attached\") s.attach = \"detached\";\n if (s.ephemeral) ephemeralDetach(s);\n wake(s);\n};\n\nconst handleFinish = (s: Session, { reason }: { reason: string }) => {\n if (reason === \"evicted\" || reason === \"error\") return;\n s.attach = \"finished\";\n settleAll(s, reason === \"superseded\" ? \"superseded\" : \"gone\");\n publish(s);\n wake(s);\n};\n\nconst isFinished = (s: Session) => s.attach === \"finished\";\n\ntype Lanes = {\n frame: StatewireFrame;\n cmdEntries: Pending[];\n ctxEntries: CtxPending[];\n};\n\n// A lane must be strictly consecutive within a frame: the first unposted run\n// per lane rides now, later runs ride follow-up frames.\nconst buildLanes = (s: Session): Lanes | null => {\n const ctxEntries: CtxPending[] = [];\n for (const entry of s.ctxPending) {\n if (entry.posted) continue;\n if (ctxEntries.length > 0 && entry.seq !== ctxEntries.at(-1)!.seq + 1)\n break;\n ctxEntries.push(entry);\n }\n const cmdEntries: Pending[] = [];\n for (const entry of s.pending) {\n if (entry.posted) continue;\n if (cmdEntries.length > 0 && entry.seq !== cmdEntries.at(-1)!.seq + 1)\n break;\n cmdEntries.push(entry);\n }\n if (ctxEntries.length === 0 && cmdEntries.length === 0) return null;\n return {\n frame: {\n ...(ctxEntries.length > 0 && {\n ctx: ctxEntries.map((c) => ({ ops: c.ops, seq: c.seq })),\n }),\n ...(cmdEntries.length > 0 && {\n cmd: cmdEntries.map((p) => ({ ...p.command, seq: p.seq })),\n }),\n },\n cmdEntries,\n ctxEntries,\n };\n};\n\nconst takeAttachLanes = (s: Session): StatewireFrame | null => {\n s.attachLaneSeqs = [new Set(), new Set()];\n const wanted = s.lanesOnAttach;\n s.lanesOnAttach = false;\n if (!wanted || !s.sessionKnown) return null;\n const lanes = buildLanes(s);\n if (!lanes) return null;\n s.attachLaneSeqs = [\n new Set(lanes.cmdEntries.map((p) => p.seq)),\n new Set(lanes.ctxEntries.map((c) => c.seq)),\n ];\n for (const entry of lanes.cmdEntries) entry.attempted = true;\n return lanes.frame;\n};\n\nconst pump = async (s: Session) => {\n if (s.pumping) return;\n s.pumping = true;\n try {\n while (s.running && !isFinished(s)) {\n const transport = s.transport;\n if (buildLanes(s) === null) return;\n if (s.attach !== \"attached\" || !transport) {\n if (transport?.connection.status === \"idle\") {\n transport.connection.reconnect();\n }\n await waitForWake(s);\n continue;\n }\n const resumeDelay = s.resumeAt - Date.now();\n if (resumeDelay > 0) {\n await sleepUntilWake(s, resumeDelay);\n continue;\n }\n const lanes = buildLanes(s);\n if (!lanes) continue;\n const epoch = s.attachEpoch;\n for (const entry of lanes.cmdEntries) entry.attempted = true;\n let result;\n try {\n result = await transport.send(lanes.frame);\n } catch {\n if (!s.running || isFinished(s)) return;\n s.frameFailures += 1;\n await sleepUntilWake(s, backoff(s.frameFailures));\n continue;\n }\n if (!s.running) return;\n s.frameFailures = 0;\n if (result.type === \"ok\") {\n if (epoch === s.attachEpoch) {\n for (const entry of lanes.cmdEntries) {\n if (s.pending.includes(entry)) {\n entry.posted = true;\n s.lifecycle?.onPosted?.(entry.inv);\n }\n }\n for (const entry of lanes.ctxEntries) entry.posted = true;\n }\n } else if (result.type === \"refusal\") {\n if (epoch === s.attachEpoch) handleRefusal(s, result.syn);\n } else if (result.type === \"stale-lease\") {\n // A POST outlived its attach: reattach first, the hello arbitrates.\n if (epoch === s.attachEpoch) {\n transport.connection.reconnect();\n await waitForWake(s);\n }\n } else if (result.type === \"no-lease\") {\n // No out-of-band write channel: the frame rides a fresh attach.\n if (epoch === s.attachEpoch) {\n s.lanesOnAttach = true;\n transport.connection.reconnect();\n while (s.running && !isFinished(s) && epoch === s.attachEpoch) {\n await waitForWake(s);\n }\n }\n }\n }\n } finally {\n s.pumping = false;\n }\n};\n\nconst start = (s: Session) => {\n if (!s.recordPersisted) {\n s.recordPersisted = true;\n s.store.save(s.record);\n }\n s.running = true;\n void pump(s);\n};\n\nconst stop = (s: Session) => {\n s.running = false;\n wake(s);\n settleAll(s, \"unmount\");\n};\n\nconst setTransport = (s: Session, transport: RemoteLink) => {\n if (s.transport === transport) return;\n s.transport = transport;\n wake(s);\n};\n\nconst enqueue = (\n s: Session,\n command: WireCommand,\n params: readonly unknown[],\n resolve: (payload: unknown) => void,\n reject: (error: StatewireSendError) => void,\n) => {\n const seq = s.record.nextSeq;\n setRecord(s, { ...s.record, nextSeq: seq + 1 });\n const entry: Pending = {\n seq,\n command,\n inv: {\n method: command.method,\n params,\n ts: Date.now(),\n seq,\n clientId: s.record.clientId,\n },\n attempted: false,\n posted: false,\n applied: false,\n resolve,\n reject,\n };\n s.pending.push(entry);\n s.lifecycle?.onStart(entry.inv);\n void pump(s);\n};\n\nconst sendCommand = (\n s: Session,\n method: string,\n params: readonly unknown[],\n): Promise<unknown> => {\n if (!s.running) {\n return rejectedSend({ fate: \"aborted\", cause: \"unmount\" });\n }\n if (isFinished(s)) {\n return rejectedSend({ fate: \"aborted\", cause: \"stream-gone\" });\n }\n const result = new Promise<unknown>((resolve, reject) => {\n const { params: encoded, meta } = encodeParams(params);\n enqueue(\n s,\n { method, params: encoded, ...(meta !== undefined && { meta }) },\n params,\n resolve,\n reject,\n );\n });\n result.catch(() => {});\n return result;\n};\n\nconst setContext = (s: Session, value: Record<string, unknown>): void => {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new Error(\"statewire: context must be a plain object\");\n }\n const { value: encoded, meta } = encodeValue(value);\n s.ctxOps = [\n {\n op: \"replace\",\n path: [],\n value: encoded,\n ...(meta !== undefined && { meta }),\n },\n ];\n const seq = s.record.ctxNextSeq;\n setRecord(s, { ...s.record, ctxNextSeq: seq + 1 });\n s.ctxPending.push({ seq, ops: s.ctxOps, posted: false });\n wake(s);\n void pump(s);\n};\n\nexport const useStatewireSession = (\n options: StatewireSessionOptions,\n): Session => {\n const [defaultStore] = useState(createMemorySessionStore);\n const store = options.sessionStore ?? defaultStore;\n const [s] = useState((): Session => {\n const stored = store.load();\n const record = stored ?? {\n clientId: newClientId(),\n nextSeq: 1,\n ctxNextSeq: 1,\n };\n const session: Session = {\n record,\n ctx: {\n get clientId() {\n return session.record.clientId;\n },\n takeAttachLanes: () => takeAttachLanes(session),\n onHello: (args) => handleHello(session, args),\n onOps: (args) => handleOps(session, args),\n onCmds: (cmds) => handleCmds(session, cmds),\n onRefusal: (syn) => handleRefusal(session, syn),\n onIdle: (level) => setIdle(session, level),\n onDetach: () => handleDetach(session),\n onFinish: (args) => handleFinish(session, args),\n },\n recordPersisted: stored !== null,\n store,\n ephemeral: false,\n sessionKnown: false,\n durableSeq: 0,\n resumeAt: 0,\n idle: false,\n onDone: undefined,\n ctxOps: undefined,\n ctxPending: [],\n lanesOnAttach: false,\n attachLaneSeqs: [new Set(), new Set()],\n frameFailures: 0,\n pending: [],\n canonical: undefined,\n lastPublished: undefined,\n changed: [],\n source: {\n snapshot: () => session.canonical,\n takeChanged: () => {\n const changed = session.changed;\n session.changed = [];\n return changed;\n },\n setOnChange: (listener) => {\n session.notify = listener ?? (() => {});\n },\n },\n attach: \"detached\",\n attachEpoch: 0,\n running: false,\n pumping: false,\n transport: null,\n waiters: [],\n lifecycle: null,\n notify: () => {},\n commands: createMethodProxy((method, params) =>\n sendCommand(session, method, params),\n ),\n };\n bindCommandsHost(session.commands, {\n setListener: (listener) => {\n session.lifecycle = listener;\n },\n });\n return session;\n });\n s.store = store;\n s.onDone = options.onDone;\n useEffect(() => {\n start(s);\n return () => stop(s);\n }, [s]);\n return s;\n};\n\nexport { setContext, setTransport };\n"],"mappings":"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"}
1
+ {"version":3,"file":"session.js","names":["useContext","useEffect","useState","applyOps","StatewirePath","StatewireCmd","StatewireSyn","encodeParams","encodeValue","reviveResponse","reviveValue","bindCommandsHost","CommandsInvocation","CommandsListener","Statewire","StatewireClient","SessionStorageContext","createMemorySessionStore","backoff","createMethodProxy","randomId","StatewireSendError","RemoteLink","StatewireFrame","StatewireRemoteProps","WireCommand","method","params","meta","Pending","seq","command","inv","attempted","posted","applied","resolve","payload","reject","error","CtxPending","ops","protocolError","message","Error","newClientId","rejectedSend","failure","SendFailure","Promise","result","catch","AttachState","StatewireSessionOptions","sessionStore","SessionStorage","onDone","Session","record","SessionRecord","ctx","recordPersisted","store","ephemeral","deactivated","sessionKnown","durableSeq","resumeAt","idle","ctxOps","ctxPending","lanesOnAttach","attachLaneSeqs","ReadonlySet","frameFailures","pending","canonical","lastPublished","changed","source","StateSource","attach","attachEpoch","running","pumping","transport","waiters","lifecycle","notify","commands","Record","publish","s","wake","waiter","waitForWake","r","push","sleepUntilWake","delayMs","clearTimeout","timer","setTimeout","filter","w","setRecord","save","backlog","SessionCommand","p","map","syncBacklog","unchanged","length","every","c","i","setIdle","level","cover","entry","onApplied","onCovered","settleEntry","outcome","ok","onSettle","type","settleAll","detach","cause","unmount","gone","superseded","const","batch","detachFailure","predecessorLost","some","fate","restateContext","undefined","ctxNextSeq","resetRecord","clientId","survivors","nextSeq","old","enqueue","ephemeralDetach","rejectedFailure","cmd","code","handleCmd","Number","isSafeInteger","dur","Math","max","find","String","handleCmds","cmds","ingestOps","state","changedPaths","publishIngested","RECORDS","snapClocks","seqWm","ctxWm","tail","cmdAligned","ctxAligned","handleHello","syn","includes","attachCmd","attachCtx","Set","has","ctxWatermark","ctxSeq","pump","handleOps","handleRefusal","watermark","retry","Date","now","firstCmd","firstCtx","clocksAhead","connection","reconnect","handleDetach","handleFinish","reason","isFinished","Lanes","frame","cmdEntries","ctxEntries","buildLanes","at","takeAttachLanes","wanted","lanes","status","resumeDelay","epoch","send","onPosted","start","resume","stop","setTransport","ts","onStart","stored","sort","a","b","sendCommand","encoded","setContext","value","Array","isArray","op","path","useStatewireSession","options","defaultStore","mounted","load","session","onHello","args","onOps","onCmds","onRefusal","onIdle","onDetach","onFinish","snapshot","takeChanged","setOnChange","listener","setListener"],"sources":["../../src/client/session.ts"],"sourcesContent":["import { useContext, useEffect, useState } from \"react\";\nimport { applyOps } from \"../wire/ops\";\nimport type { StatewirePath } from \"../wire/ops\";\nimport type { StatewireCmd, StatewireSyn } from \"./connection\";\nimport {\n encodeParams,\n encodeValue,\n reviveResponse,\n reviveValue,\n} from \"../wire/meta\";\nimport { bindCommandsHost } from \"../host/host\";\nimport type { CommandsInvocation, CommandsListener } from \"../host/host\";\nimport type { Statewire } from \"../types\";\nimport type { StatewireClient } from \"../statewire\";\nimport {\n SessionStorageContext,\n createMemorySessionStore,\n} from \"./sessionStore\";\nimport { backoff } from \"./timing\";\nimport { createMethodProxy } from \"../util/methodProxy\";\nimport { randomId } from \"../util/randomId\";\nimport { StatewireSendError } from \"../types\";\nimport type {\n RemoteLink,\n StatewireFrame,\n StatewireRemoteProps,\n} from \"../transports/sse\";\n\ntype WireCommand = {\n method: string;\n params: unknown[];\n meta?: unknown;\n};\n\ntype Pending = {\n readonly seq: number;\n readonly command: WireCommand;\n readonly inv: CommandsInvocation;\n /** A send carrying this statement may have reached the server. */\n attempted: boolean;\n /** In the resend queue when false. */\n posted: boolean;\n /** The server acknowledged admission; the terminal verdict is still due. */\n applied: boolean;\n resolve: (payload: unknown) => void;\n reject: (error: StatewireSendError) => void;\n};\n\ntype CtxPending = {\n readonly seq: number;\n readonly ops: unknown[];\n posted: boolean;\n};\n\nconst protocolError = (message: string): never => {\n throw new Error(`statewire: protocol error: ${message}`);\n};\n\nconst newClientId = () => `c-${randomId()}`;\n\nconst rejectedSend = (\n failure: StatewireClient.SendFailure,\n): Promise<unknown> => {\n const result = Promise.reject<unknown>(new StatewireSendError(failure));\n result.catch(() => {});\n return result;\n};\n\ntype AttachState = \"detached\" | \"attached\" | \"finished\";\n\nexport type StatewireSessionOptions = {\n sessionStore?: StatewireClient.SessionStorage | undefined;\n onDone?: (() => void) | undefined;\n};\n\ntype Session = {\n record: StatewireClient.SessionRecord;\n ctx: StatewireRemoteProps;\n recordPersisted: boolean;\n store: StatewireClient.SessionStorage;\n ephemeral: boolean;\n /** An ephemeral record died with its attach: the storage is off for good. */\n deactivated: boolean;\n sessionKnown: boolean;\n durableSeq: number;\n resumeAt: number;\n idle: boolean;\n onDone: (() => void) | undefined;\n ctxOps: unknown[] | undefined;\n ctxPending: CtxPending[];\n lanesOnAttach: boolean;\n attachLaneSeqs: readonly [ReadonlySet<number>, ReadonlySet<number>];\n frameFailures: number;\n pending: Pending[];\n canonical: unknown;\n lastPublished: unknown;\n changed: StatewirePath[];\n source: Statewire.StateSource;\n attach: AttachState;\n attachEpoch: number;\n running: boolean;\n pumping: boolean;\n transport: RemoteLink | null;\n waiters: (() => void)[];\n lifecycle: CommandsListener | null;\n notify: () => void;\n commands: Record<string, (...params: unknown[]) => Promise<unknown>>;\n};\n\nconst publish = (s: Session) => {\n if (s.canonical === s.lastPublished) return;\n s.lastPublished = s.canonical;\n s.notify();\n};\n\nconst wake = (s: Session) => {\n const waiters = s.waiters;\n s.waiters = [];\n for (const waiter of waiters) waiter();\n};\n\nconst waitForWake = (s: Session) => new Promise<void>((r) => s.waiters.push(r));\n\nconst sleepUntilWake = (s: Session, delayMs: number) =>\n new Promise<void>((resolve) => {\n const waiter = () => {\n clearTimeout(timer);\n resolve();\n };\n const timer = setTimeout(() => {\n s.waiters = s.waiters.filter((w) => w !== waiter);\n resolve();\n }, delayMs);\n s.waiters.push(waiter);\n });\n\nconst setRecord = (s: Session, record: StatewireClient.SessionRecord) => {\n s.record = record;\n if (!s.deactivated) s.store.save(record);\n};\n\nconst backlog = (s: Session): StatewireClient.SessionCommand[] =>\n s.pending\n .filter((p) => p.seq > s.durableSeq)\n .map((p) => ({ seq: p.seq, ...p.command }));\n\n// Storage holds what a reload must resend: a command leaves at its dur, at a\n// server verdict, or at a record reset — never at a local channel death.\nconst syncBacklog = (s: Session) => {\n const commands = backlog(s);\n const unchanged =\n commands.length === s.record.commands.length &&\n commands.every((c, i) => c.seq === s.record.commands[i]!.seq);\n if (unchanged) return;\n setRecord(s, { ...s.record, commands });\n};\n\nconst setIdle = (s: Session, level: boolean) => {\n if (level === s.idle) return;\n s.idle = level;\n s.notify();\n if (level) s.onDone?.();\n};\n\nconst cover = (s: Session, entry: Pending) => {\n if (entry.applied) return;\n entry.applied = true;\n s.lifecycle?.onApplied(entry.inv);\n s.lifecycle?.onCovered?.(entry.inv);\n};\n\nconst settleEntry = (\n s: Session,\n entry: Pending,\n outcome:\n | { ok: true; payload: unknown }\n | { ok: false; failure: StatewireClient.SendFailure },\n) => {\n s.pending = s.pending.filter((p) => p !== entry);\n if (outcome.ok) {\n entry.resolve(outcome.payload);\n s.lifecycle?.onSettle(entry.inv, {\n type: \"accepted\",\n payload: outcome.payload,\n });\n } else {\n entry.reject(new StatewireSendError(outcome.failure));\n s.lifecycle?.onSettle(entry.inv, {\n type: \"rejected\",\n error: new StatewireSendError(outcome.failure),\n });\n }\n};\n\nconst settleAll = (s: Session, detach: \"unmount\" | \"gone\" | \"superseded\") => {\n const cause = (\n {\n unmount: \"unmount\",\n gone: \"stream-gone\",\n superseded: \"superseded\",\n } as const\n )[detach];\n const batch = s.pending;\n const detachFailure = (entry: Pending): StatewireClient.SendFailure => {\n if (!entry.attempted) {\n const predecessorLost = batch.some(\n (p) => p.seq < entry.seq && p.attempted,\n );\n return predecessorLost\n ? { fate: \"aborted\", cause, predecessorLost }\n : { fate: \"aborted\", cause };\n }\n return { fate: \"lost\", cause };\n };\n for (const entry of [...batch]) {\n s.lifecycle?.onCovered?.(entry.inv);\n settleEntry(s, entry, { ok: false, failure: detachFailure(entry) });\n }\n};\n\nconst restateContext = (s: Session) => {\n s.ctxPending = [];\n if (s.ctxOps === undefined) return;\n const seq = s.record.ctxNextSeq;\n setRecord(s, { ...s.record, ctxNextSeq: seq + 1 });\n s.ctxPending.push({ seq, ops: s.ctxOps, posted: false });\n};\n\n// The server-side record's dedup memory is gone: history that may have\n// reached a server settles lost — the new hello's snapshot is the only\n// truth about what survived — and never-attempted statements re-stamp\n// from 1 on the reset lane clocks.\nconst resetRecord = (s: Session, clientId: string) => {\n const survivors: Pending[] = [];\n for (const entry of [...s.pending]) {\n if (entry.attempted || entry.applied) {\n s.lifecycle?.onCovered?.(entry.inv);\n settleEntry(s, entry, {\n ok: false,\n failure: { fate: \"lost\", cause: \"session-lost\" },\n });\n } else {\n survivors.push(entry);\n }\n }\n s.pending = [];\n s.durableSeq = 0;\n setRecord(s, { clientId, nextSeq: 1, ctxNextSeq: 1, commands: [] });\n for (const old of survivors) {\n s.lifecycle?.onCovered?.(old.inv);\n enqueue(s, old.command, old.inv.params, old.resolve, old.reject);\n }\n restateContext(s);\n publish(s);\n wake(s);\n};\n\n// The record died with the attach: mint a fresh id for the next one.\nconst ephemeralDetach = (s: Session) => {\n s.ephemeral = false;\n s.sessionKnown = false;\n resetRecord(s, newClientId());\n};\n\nconst rejectedFailure = (cmd: StatewireCmd): StatewireClient.SendFailure => {\n return {\n fate: \"rejected\",\n cause: \"server\",\n ...(cmd.code !== undefined && { code: cmd.code }),\n ...(cmd.message !== undefined && { message: cmd.message }),\n ...(cmd.payload !== undefined && { payload: cmd.payload }),\n };\n};\n\nconst handleCmd = (s: Session, cmd: StatewireCmd) => {\n if (!Number.isSafeInteger(cmd.seq) || cmd.seq <= 0) {\n protocolError(\"cmd.seq must be a positive safe integer\");\n }\n if (cmd.dur === true) {\n s.durableSeq = Math.max(s.durableSeq, cmd.seq);\n for (const entry of s.pending) {\n if (entry.seq <= s.durableSeq) entry.posted = true;\n }\n }\n if (cmd.type === undefined) return;\n const entry = s.pending.find((p) => p.seq === cmd.seq);\n if (cmd.type === \"applied\") {\n if (entry) cover(s, entry);\n return;\n }\n if (!entry) return;\n cover(s, entry);\n const outcome =\n cmd.type === \"result\"\n ? { ok: true as const, payload: reviveResponse(cmd).payload }\n : cmd.type === \"rejected\"\n ? { ok: false as const, failure: rejectedFailure(reviveResponse(cmd)) }\n : cmd.type === \"failed\"\n ? {\n ok: false as const,\n failure: {\n fate: \"failed\" as const,\n cause: \"server\" as const,\n ...(cmd.message !== undefined && { message: cmd.message }),\n },\n }\n : cmd.type === \"crashed\"\n ? {\n ok: false as const,\n failure: { fate: \"crashed\" as const, cause: \"server\" as const },\n }\n : cmd.type === \"expired\"\n ? {\n ok: false as const,\n failure: {\n fate: \"expired\" as const,\n cause: \"server\" as const,\n },\n }\n : protocolError(`unknown cmd type: ${String(cmd.type)}`);\n settleEntry(s, entry, outcome);\n};\n\nconst handleCmds = (s: Session, cmds: StatewireCmd[]) => {\n for (const cmd of cmds) handleCmd(s, cmd);\n syncBacklog(s);\n wake(s);\n};\n\nconst ingestOps = (s: Session, ops: unknown[]) => {\n const applied = applyOps(s.canonical, ops);\n s.canonical = applied.state;\n s.changed.push(...applied.changedPaths);\n};\n\nconst publishIngested = (s: Session) => {\n publish(s);\n wake(s);\n};\n\nconst RECORDS = [\"fresh\", \"resumed\", \"ephemeral\"];\n\n// Snap a resumed session's lane clocks onto the record: seqs above the stated\n// watermark were never admitted, so a stale restored clock re-stamps its\n// never-admitted statements consecutively after the watermark.\nconst snapClocks = (s: Session, seqWm: number, ctxWm: number) => {\n const tail = s.pending.filter((p) => p.seq > seqWm);\n const cmdAligned =\n tail.every((p, i) => p.seq === seqWm + 1 + i) &&\n s.record.nextSeq === seqWm + tail.length + 1;\n if (!cmdAligned) {\n s.pending = s.pending.filter((p) => p.seq <= seqWm);\n setRecord(s, { ...s.record, nextSeq: seqWm + 1 });\n for (const old of tail) {\n s.lifecycle?.onCovered?.(old.inv);\n enqueue(s, old.command, old.inv.params, old.resolve, old.reject);\n }\n }\n const ctxAligned =\n s.ctxPending.every((c, i) => c.seq === ctxWm + 1 + i) &&\n s.record.ctxNextSeq === ctxWm + s.ctxPending.length + 1;\n if (!ctxAligned) {\n setRecord(s, { ...s.record, ctxNextSeq: ctxWm + 1 });\n restateContext(s);\n }\n};\n\nconst handleHello = (\n s: Session,\n { ops, syn }: { ops: unknown[]; syn: StatewireSyn },\n) => {\n const record = syn.record ?? \"fresh\";\n if (!RECORDS.includes(record)) {\n protocolError(`unknown record: ${String(record)}`);\n }\n ingestOps(s, ops);\n s.attach = \"attached\";\n s.attachEpoch += 1;\n s.frameFailures = 0;\n s.resumeAt = 0;\n s.ephemeral = record === \"ephemeral\";\n if (s.ephemeral) s.deactivated = true;\n const [attachCmd, attachCtx] = s.attachLaneSeqs;\n s.attachLaneSeqs = [new Set(), new Set()];\n if (record === \"resumed\") {\n // Resending unsettled statements is exactly-once-safe: absorbed replays\n // re-emit their stored verdicts (applied while still in flight).\n for (const entry of s.pending) {\n entry.posted = attachCmd.has(entry.seq);\n }\n const ctxWatermark = syn.ctxSeq ?? 0;\n s.ctxPending = s.ctxPending.filter((c) => c.seq > ctxWatermark);\n for (const c of s.ctxPending) c.posted = attachCtx.has(c.seq);\n snapClocks(s, syn.seq ?? 0, ctxWatermark);\n syncBacklog(s);\n } else {\n resetRecord(s, s.record.clientId);\n }\n s.sessionKnown = true;\n publishIngested(s);\n void pump(s);\n};\n\nconst handleOps = (\n s: Session,\n { ops, cmds }: { ops: unknown[]; cmds: StatewireCmd[] },\n) => {\n ingestOps(s, ops);\n for (const cmd of cmds) handleCmd(s, cmd);\n syncBacklog(s);\n publishIngested(s);\n};\n\n// A {syn} refusal never settles: reconcile the resend queues against the\n// stated watermarks; retry present means wait, absent means resend now.\nconst handleRefusal = (s: Session, syn: StatewireSyn) => {\n const { seq, ctxSeq } = syn;\n if (typeof seq === \"number\") {\n const watermark = Math.max(seq, s.durableSeq);\n for (const entry of s.pending) entry.posted = entry.seq <= watermark;\n }\n if (typeof ctxSeq === \"number\") {\n s.ctxPending = s.ctxPending.filter((c) => c.seq > ctxSeq);\n for (const c of s.ctxPending) c.posted = false;\n }\n if (typeof syn.retry === \"number\") {\n s.resumeAt = Date.now() + syn.retry;\n }\n const firstCmd = s.pending.find((p) => !p.posted);\n const firstCtx = s.ctxPending.find((c) => !c.posted);\n const clocksAhead =\n (typeof syn.seq === \"number\" &&\n firstCmd !== undefined &&\n firstCmd.seq > Math.max(syn.seq, s.durableSeq) + 1) ||\n (typeof syn.ctxSeq === \"number\" &&\n firstCtx !== undefined &&\n firstCtx.seq > syn.ctxSeq + 1);\n // Clocks ahead of a record that cannot admit them: reattach first and let\n // the hello's record arbitrate.\n if (clocksAhead) s.transport?.connection.reconnect();\n wake(s);\n void pump(s);\n};\n\nconst handleDetach = (s: Session) => {\n if (s.attach === \"attached\") s.attach = \"detached\";\n if (s.ephemeral) ephemeralDetach(s);\n wake(s);\n};\n\nconst handleFinish = (s: Session, { reason }: { reason: string }) => {\n if (reason === \"evicted\" || reason === \"error\") return;\n s.attach = \"finished\";\n settleAll(s, reason === \"superseded\" ? \"superseded\" : \"gone\");\n publish(s);\n wake(s);\n};\n\nconst isFinished = (s: Session) => s.attach === \"finished\";\n\ntype Lanes = {\n frame: StatewireFrame;\n cmdEntries: Pending[];\n ctxEntries: CtxPending[];\n};\n\n// A lane must be strictly consecutive within a frame: the first unposted run\n// per lane rides now, later runs ride follow-up frames.\nconst buildLanes = (s: Session): Lanes | null => {\n const ctxEntries: CtxPending[] = [];\n for (const entry of s.ctxPending) {\n if (entry.posted) continue;\n if (ctxEntries.length > 0 && entry.seq !== ctxEntries.at(-1)!.seq + 1)\n break;\n ctxEntries.push(entry);\n }\n const cmdEntries: Pending[] = [];\n for (const entry of s.pending) {\n if (entry.posted) continue;\n if (cmdEntries.length > 0 && entry.seq !== cmdEntries.at(-1)!.seq + 1)\n break;\n cmdEntries.push(entry);\n }\n if (ctxEntries.length === 0 && cmdEntries.length === 0) return null;\n return {\n frame: {\n ...(ctxEntries.length > 0 && {\n ctx: ctxEntries.map((c) => ({ ops: c.ops, seq: c.seq })),\n }),\n ...(cmdEntries.length > 0 && {\n cmd: cmdEntries.map((p) => ({ ...p.command, seq: p.seq })),\n }),\n },\n cmdEntries,\n ctxEntries,\n };\n};\n\nconst takeAttachLanes = (s: Session): StatewireFrame | null => {\n s.attachLaneSeqs = [new Set(), new Set()];\n const wanted = s.lanesOnAttach;\n s.lanesOnAttach = false;\n if (!wanted || !s.sessionKnown) return null;\n const lanes = buildLanes(s);\n if (!lanes) return null;\n s.attachLaneSeqs = [\n new Set(lanes.cmdEntries.map((p) => p.seq)),\n new Set(lanes.ctxEntries.map((c) => c.seq)),\n ];\n for (const entry of lanes.cmdEntries) entry.attempted = true;\n return lanes.frame;\n};\n\nconst pump = async (s: Session) => {\n if (s.pumping) return;\n s.pumping = true;\n try {\n while (s.running && !isFinished(s)) {\n const transport = s.transport;\n if (buildLanes(s) === null) return;\n if (s.attach !== \"attached\" || !transport) {\n if (transport?.connection.status === \"idle\") {\n transport.connection.reconnect();\n }\n await waitForWake(s);\n continue;\n }\n const resumeDelay = s.resumeAt - Date.now();\n if (resumeDelay > 0) {\n await sleepUntilWake(s, resumeDelay);\n continue;\n }\n const lanes = buildLanes(s);\n if (!lanes) continue;\n const epoch = s.attachEpoch;\n for (const entry of lanes.cmdEntries) entry.attempted = true;\n let result;\n try {\n result = await transport.send(lanes.frame);\n } catch {\n if (!s.running || isFinished(s)) return;\n s.frameFailures += 1;\n await sleepUntilWake(s, backoff(s.frameFailures));\n continue;\n }\n if (!s.running) return;\n s.frameFailures = 0;\n if (result.type === \"ok\") {\n if (epoch === s.attachEpoch) {\n for (const entry of lanes.cmdEntries) {\n if (s.pending.includes(entry)) {\n entry.posted = true;\n s.lifecycle?.onPosted?.(entry.inv);\n }\n }\n for (const entry of lanes.ctxEntries) entry.posted = true;\n }\n } else if (result.type === \"refusal\") {\n if (epoch === s.attachEpoch) handleRefusal(s, result.syn);\n } else if (result.type === \"stale-lease\") {\n // A POST outlived its attach: reattach first, the hello arbitrates.\n if (epoch === s.attachEpoch) {\n transport.connection.reconnect();\n await waitForWake(s);\n }\n } else if (result.type === \"no-lease\") {\n // No out-of-band write channel: the frame rides a fresh attach.\n if (epoch === s.attachEpoch) {\n s.lanesOnAttach = true;\n transport.connection.reconnect();\n while (s.running && !isFinished(s) && epoch === s.attachEpoch) {\n await waitForWake(s);\n }\n }\n }\n }\n } finally {\n s.pumping = false;\n }\n};\n\nconst start = (s: Session) => {\n if (!s.recordPersisted) {\n s.recordPersisted = true;\n s.store.save(s.record);\n }\n resume(s);\n s.running = true;\n void pump(s);\n};\n\nconst stop = (s: Session) => {\n s.running = false;\n wake(s);\n settleAll(s, \"unmount\");\n};\n\nconst setTransport = (s: Session, transport: RemoteLink) => {\n if (s.transport === transport) return;\n s.transport = transport;\n wake(s);\n};\n\nconst enqueue = (\n s: Session,\n command: WireCommand,\n params: readonly unknown[],\n resolve: (payload: unknown) => void,\n reject: (error: StatewireSendError) => void,\n) => {\n const seq = s.record.nextSeq;\n const entry: Pending = {\n seq,\n command,\n inv: {\n method: command.method,\n params,\n ts: Date.now(),\n seq,\n clientId: s.record.clientId,\n },\n attempted: false,\n posted: false,\n applied: false,\n resolve,\n reject,\n };\n s.pending.push(entry);\n setRecord(s, { ...s.record, nextSeq: seq + 1, commands: backlog(s) });\n s.lifecycle?.onStart(entry.inv);\n void pump(s);\n};\n\n// A stored command cannot be told from one that reached the server: attempted\n// keeps a fresh record settling it lost instead of replaying it blind.\nconst resume = (s: Session) => {\n const stored = [...s.record.commands].sort((a, b) => a.seq - b.seq);\n for (const { seq, ...command } of stored) {\n const params =\n command.meta === undefined\n ? command.params\n : (reviveValue([...command.params], command.meta) as unknown[]);\n const entry: Pending = {\n seq,\n command,\n inv: {\n method: command.method,\n params,\n ts: Date.now(),\n seq,\n clientId: s.record.clientId,\n },\n attempted: true,\n posted: false,\n applied: false,\n resolve: () => {},\n reject: () => {},\n };\n s.pending.push(entry);\n s.lifecycle?.onStart(entry.inv);\n }\n};\n\nconst sendCommand = (\n s: Session,\n method: string,\n params: readonly unknown[],\n): Promise<unknown> => {\n if (!s.running) {\n return rejectedSend({ fate: \"aborted\", cause: \"unmount\" });\n }\n if (isFinished(s)) {\n return rejectedSend({ fate: \"aborted\", cause: \"stream-gone\" });\n }\n const result = new Promise<unknown>((resolve, reject) => {\n const { params: encoded, meta } = encodeParams(params);\n enqueue(\n s,\n { method, params: encoded, ...(meta !== undefined && { meta }) },\n params,\n resolve,\n reject,\n );\n });\n result.catch(() => {});\n return result;\n};\n\nconst setContext = (s: Session, value: Record<string, unknown>): void => {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new Error(\"statewire: context must be a plain object\");\n }\n const { value: encoded, meta } = encodeValue(value);\n s.ctxOps = [\n {\n op: \"replace\",\n path: [],\n value: encoded,\n ...(meta !== undefined && { meta }),\n },\n ];\n const seq = s.record.ctxNextSeq;\n setRecord(s, { ...s.record, ctxNextSeq: seq + 1 });\n s.ctxPending.push({ seq, ops: s.ctxOps, posted: false });\n wake(s);\n void pump(s);\n};\n\nexport const useStatewireSession = (\n options: StatewireSessionOptions,\n): Session => {\n const [defaultStore] = useState(createMemorySessionStore);\n const mounted = useContext(SessionStorageContext);\n if (options.sessionStore !== undefined && mounted !== undefined) {\n throw new Error(\n \"statewire: session storage is set twice — drop the transport's sessionStore or the client's storage.session\",\n );\n }\n const store = options.sessionStore ?? mounted ?? defaultStore;\n const [s] = useState((): Session => {\n const stored = store.load();\n if (stored !== null && !Array.isArray(stored.commands)) {\n throw new Error(\n \"statewire: session storage loaded a record without a commands array\",\n );\n }\n const record = stored ?? {\n clientId: newClientId(),\n nextSeq: 1,\n ctxNextSeq: 1,\n commands: [],\n };\n const session: Session = {\n record,\n ctx: {\n get clientId() {\n return session.record.clientId;\n },\n takeAttachLanes: () => takeAttachLanes(session),\n onHello: (args) => handleHello(session, args),\n onOps: (args) => handleOps(session, args),\n onCmds: (cmds) => handleCmds(session, cmds),\n onRefusal: (syn) => handleRefusal(session, syn),\n onIdle: (level) => setIdle(session, level),\n onDetach: () => handleDetach(session),\n onFinish: (args) => handleFinish(session, args),\n },\n recordPersisted: stored !== null,\n store,\n ephemeral: false,\n deactivated: false,\n sessionKnown: false,\n durableSeq: 0,\n resumeAt: 0,\n idle: false,\n onDone: undefined,\n ctxOps: undefined,\n ctxPending: [],\n lanesOnAttach: false,\n attachLaneSeqs: [new Set(), new Set()],\n frameFailures: 0,\n pending: [],\n canonical: undefined,\n lastPublished: undefined,\n changed: [],\n source: {\n snapshot: () => session.canonical,\n takeChanged: () => {\n const changed = session.changed;\n session.changed = [];\n return changed;\n },\n setOnChange: (listener) => {\n session.notify = listener ?? (() => {});\n },\n },\n attach: \"detached\",\n attachEpoch: 0,\n running: false,\n pumping: false,\n transport: null,\n waiters: [],\n lifecycle: null,\n notify: () => {},\n commands: createMethodProxy((method, params) =>\n sendCommand(session, method, params),\n ),\n };\n bindCommandsHost(session.commands, {\n setListener: (listener) => {\n session.lifecycle = listener;\n },\n });\n return session;\n });\n s.store = store;\n s.onDone = options.onDone;\n useEffect(() => {\n start(s);\n return () => stop(s);\n }, [s]);\n return s;\n};\n\nexport { setContext, setTransport };\n"],"mappings":"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"}
@@ -1,17 +1,8 @@
1
+ import { StatewireClient } from "../statewire.js";
1
2
  //#region src/client/sessionStore.d.ts
2
- type StatewireSessionRecord = {
3
- clientId: string;
4
- nextSeq: number;
5
- ctxNextSeq: number;
6
- };
7
- type StatewireSessionStore = {
8
- load: () => StatewireSessionRecord | null;
9
- save: (record: StatewireSessionRecord) => void;
10
- };
11
- type StatewireSessionStoreOptions = {
12
- sessionStore?: StatewireSessionStore | undefined;
13
- };
14
- declare const createMemorySessionStore: () => StatewireSessionStore;
3
+ /** The session storage `useStatewire` mounts, read by the transport's session. */
4
+ declare const SessionStorageContext: import("react").Context<StatewireClient.SessionStorage | undefined>;
5
+ declare const createMemorySessionStore: () => StatewireClient.SessionStorage;
15
6
  //#endregion
16
- export { StatewireSessionRecord, StatewireSessionStore, StatewireSessionStoreOptions, createMemorySessionStore };
7
+ export { SessionStorageContext, createMemorySessionStore };
17
8
  //# sourceMappingURL=sessionStore.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sessionStore.d.ts","names":[],"sources":["../../src/client/sessionStore.ts"],"mappings":";KAAY;EACV;EACA;EACA;;KAGU;EACV,YAAY;EACZ,OAAO,QAAQ;;KAGL;EACV,eAAe;;cAGJ,gCAA+B"}
1
+ {"version":3,"file":"sessionStore.d.ts","names":[],"sources":["../../src/client/sessionStore.ts"],"mappings":";;;cAIa,uCAAqB,QAAA,gBAAA;cAIrB,gCAA+B,gBAAgB"}
@@ -1,4 +1,7 @@
1
+ import { createContext } from "@assistant-ui/tap/react-shim";
1
2
  //#region src/client/sessionStore.ts
3
+ /** The session storage `useStatewire` mounts, read by the transport's session. */
4
+ const SessionStorageContext = createContext(void 0);
2
5
  const createMemorySessionStore = () => {
3
6
  let record = null;
4
7
  return {
@@ -9,6 +12,6 @@ const createMemorySessionStore = () => {
9
12
  };
10
13
  };
11
14
  //#endregion
12
- export { createMemorySessionStore };
15
+ export { SessionStorageContext, createMemorySessionStore };
13
16
 
14
17
  //# sourceMappingURL=sessionStore.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sessionStore.js","names":["StatewireSessionRecord","clientId","nextSeq","ctxNextSeq","StatewireSessionStore","load","save","record","StatewireSessionStoreOptions","sessionStore","createMemorySessionStore","next"],"sources":["../../src/client/sessionStore.ts"],"sourcesContent":["export type StatewireSessionRecord = {\n clientId: string;\n nextSeq: number;\n ctxNextSeq: number;\n};\n\nexport type StatewireSessionStore = {\n load: () => StatewireSessionRecord | null;\n save: (record: StatewireSessionRecord) => void;\n};\n\nexport type StatewireSessionStoreOptions = {\n sessionStore?: StatewireSessionStore | undefined;\n};\n\nexport const createMemorySessionStore = (): StatewireSessionStore => {\n let record: StatewireSessionRecord | null = null;\n return {\n load: () => record,\n save: (next) => {\n record = next;\n },\n };\n};\n"],"mappings":";AAeA,MAAaU,iCAAwD;CACnE,IAAIH,SAAwC;CAC5C,OAAO;EACLF,YAAYE;EACZD,OAAOK,SAAS;GACdJ,SAASI;EACX;CACF;AACF"}
1
+ {"version":3,"file":"sessionStore.js","names":["createContext","StatewireClient","SessionStorageContext","SessionStorage","undefined","createMemorySessionStore","record","SessionRecord","load","save","next"],"sources":["../../src/client/sessionStore.ts"],"sourcesContent":["import { createContext } from \"react\";\nimport type { StatewireClient } from \"../statewire\";\n\n/** The session storage `useStatewire` mounts, read by the transport's session. */\nexport const SessionStorageContext = createContext<\n StatewireClient.SessionStorage | undefined\n>(undefined);\n\nexport const createMemorySessionStore = (): StatewireClient.SessionStorage => {\n let record: StatewireClient.SessionRecord | null = null;\n return {\n load: () => record,\n save: (next) => {\n record = next;\n },\n };\n};\n"],"mappings":";;;AAIA,MAAaE,wBAAwBF,cAEnCI,KAAAA,CAAS;AAEX,MAAaC,iCAAiE;CAC5E,IAAIC,SAA+C;CACnD,OAAO;EACLE,YAAYF;EACZG,OAAOC,SAAS;GACdJ,SAASI;EACX;CACF;AACF"}
@@ -2,7 +2,9 @@
2
2
  declare const timing: {
3
3
  backoffBaseMs: number;
4
4
  backoffMaxMs: number;
5
+ backoffResetMs: number;
5
6
  heartbeatTimeoutMs: number;
7
+ random: () => number;
6
8
  };
7
9
  declare const backoff: (attempt: number) => number;
8
10
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"timing.d.ts","names":[],"sources":["../../src/client/timing.ts"],"mappings":";cAAa;;;;;cAMA,UAAW"}
1
+ {"version":3,"file":"timing.d.ts","names":[],"sources":["../../src/client/timing.ts"],"mappings":";cAAa;;;;;;;cAQA,UAAW"}
@@ -2,9 +2,15 @@
2
2
  const timing = {
3
3
  backoffBaseMs: 250,
4
4
  backoffMaxMs: 3e4,
5
- heartbeatTimeoutMs: 3e4
5
+ backoffResetMs: 3e4,
6
+ heartbeatTimeoutMs: 3e4,
7
+ random: Math.random
8
+ };
9
+ const backoff = (attempt) => {
10
+ const limit = Math.min(timing.backoffBaseMs * 2 ** (attempt - 1), timing.backoffMaxMs);
11
+ const floor = Math.min(timing.backoffBaseMs, limit);
12
+ return Math.round(floor + (limit - floor) * timing.random());
6
13
  };
7
- const backoff = (attempt) => Math.min(timing.backoffBaseMs * 2 ** (attempt - 1), timing.backoffMaxMs);
8
14
  //#endregion
9
15
  export { backoff, timing };
10
16
 
@@ -1 +1 @@
1
- {"version":3,"file":"timing.js","names":["timing","backoffBaseMs","backoffMaxMs","heartbeatTimeoutMs","backoff","attempt","Math","min"],"sources":["../../src/client/timing.ts"],"sourcesContent":["export const timing = {\n backoffBaseMs: 250,\n backoffMaxMs: 30_000,\n heartbeatTimeoutMs: 30_000,\n};\n\nexport const backoff = (attempt: number) =>\n Math.min(timing.backoffBaseMs * 2 ** (attempt - 1), timing.backoffMaxMs);\n"],"mappings":";AAAA,MAAaA,SAAS;CACpBC,eAAe;CACfC,cAAc;CACdC,oBAAoB;AACtB;AAEA,MAAaC,WAAWC,YACtBC,KAAKC,IAAIP,OAAOC,gBAAgB,MAAMI,UAAU,IAAIL,OAAOE,YAAY"}
1
+ {"version":3,"file":"timing.js","names":["timing","backoffBaseMs","backoffMaxMs","backoffResetMs","heartbeatTimeoutMs","random","Math","backoff","attempt","limit","min","floor","round"],"sources":["../../src/client/timing.ts"],"sourcesContent":["export const timing = {\n backoffBaseMs: 250,\n backoffMaxMs: 30_000,\n backoffResetMs: 30_000,\n heartbeatTimeoutMs: 30_000,\n random: Math.random,\n};\n\nexport const backoff = (attempt: number) => {\n const limit = Math.min(\n timing.backoffBaseMs * 2 ** (attempt - 1),\n timing.backoffMaxMs,\n );\n const floor = Math.min(timing.backoffBaseMs, limit);\n return Math.round(floor + (limit - floor) * timing.random());\n};\n"],"mappings":";AAAA,MAAaA,SAAS;CACpBC,eAAe;CACfC,cAAc;CACdC,gBAAgB;CAChBC,oBAAoB;CACpBC,QAAQC,KAAKD;AACf;AAEA,MAAaE,WAAWC,YAAoB;CAC1C,MAAMC,QAAQH,KAAKI,IACjBV,OAAOC,gBAAgB,MAAMO,UAAU,IACvCR,OAAOE,YACT;CACA,MAAMS,QAAQL,KAAKI,IAAIV,OAAOC,eAAeQ,KAAK;CAClD,OAAOH,KAAKM,MAAMD,SAASF,QAAQE,SAASX,OAAOK,OAAO,CAAC;AAC7D"}
@@ -0,0 +1,7 @@
1
+ import { SocketHostState } from "./socket.js";
2
+ //#region src/host/http.d.ts
3
+ declare const stream: (h: SocketHostState, request: Request) => Promise<Response>;
4
+ declare const frames: (h: SocketHostState, request: Request) => Promise<Response>;
5
+ //#endregion
6
+ export { frames, stream };
7
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","names":[],"sources":["../../src/host/http.ts"],"mappings":";;cAiNa,SACX,GAAG,iBACH,SAAS,YACR,QAAQ;cAkCE,SACX,GAAG,iBACH,SAAS,YACR,QAAQ"}
@@ -0,0 +1,175 @@
1
+ import { CLIENT_ID_RE, admitFrame, closeConn, flush, parseFrame, parseFrameValue, register, sendTo } from "./socket.js";
2
+ //#region src/host/http.ts
3
+ const CLIENT_ID_HEADER = "Statewire-Client-Id";
4
+ const LEASE_HEADER = "Statewire-Lease";
5
+ const VERSION_HEADER = "Statewire-Version";
6
+ const VERSION = "1";
7
+ const encoder = new TextEncoder();
8
+ const invalidClientIdReason = (clientId) => {
9
+ if (clientId === "") return "id must be non-empty";
10
+ if (clientId.length > 256) return "id must be at most 256 characters";
11
+ if (!CLIENT_ID_RE.test(clientId)) return "id must contain only [A-Za-z0-9._~-]";
12
+ return null;
13
+ };
14
+ const clientIdResponse = (request) => {
15
+ const clientId = request.headers.get(CLIENT_ID_HEADER);
16
+ if (clientId === null) return { response: Response.json({ detail: `missing ${CLIENT_ID_HEADER} header` }, { status: 400 }) };
17
+ const reason = invalidClientIdReason(clientId);
18
+ if (reason !== null) return { response: Response.json({ detail: `invalid ${CLIENT_ID_HEADER}: ${reason}` }, { status: 400 }) };
19
+ return { clientId };
20
+ };
21
+ const headersOf = (request) => Object.fromEntries(request.headers);
22
+ const mergedHeaders = (request, extra) => {
23
+ const headers = headersOf(request);
24
+ for (const [name, value] of Object.entries(extra)) headers[name.toLowerCase()] = value;
25
+ return headers;
26
+ };
27
+ const authorize = async (h, headers) => {
28
+ try {
29
+ await h.authorize?.({ headers });
30
+ return null;
31
+ } catch (error) {
32
+ if (error instanceof Response) return error;
33
+ throw error;
34
+ }
35
+ };
36
+ const mintLease = () => {
37
+ const bytes = crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(16));
38
+ return btoa(String.fromCharCode(...bytes)).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
39
+ };
40
+ const attachSse = (h, clientId) => {
41
+ let controller;
42
+ let connection = null;
43
+ let open = true;
44
+ const body = new ReadableStream({
45
+ start: (next) => {
46
+ controller = next;
47
+ },
48
+ cancel: () => {
49
+ open = false;
50
+ connection?.close();
51
+ }
52
+ });
53
+ const registration = register(h, clientId, {
54
+ send: (data) => {
55
+ if (!open) return;
56
+ const frame = data === "{}" ? ": ping\n\n" : `data: ${data}\n\n`;
57
+ try {
58
+ controller.enqueue(encoder.encode(frame));
59
+ } catch {
60
+ open = false;
61
+ connection?.close();
62
+ }
63
+ },
64
+ close: () => {
65
+ if (!open) return;
66
+ open = false;
67
+ try {
68
+ controller.close();
69
+ } catch {
70
+ return;
71
+ }
72
+ }
73
+ });
74
+ connection = registration?.connection ?? null;
75
+ if (!open) connection?.close();
76
+ return {
77
+ body,
78
+ registration
79
+ };
80
+ };
81
+ const streamResponse = (attachment, lease) => new Response(attachment.body, {
82
+ status: 200,
83
+ headers: {
84
+ "Content-Type": "text/event-stream",
85
+ [VERSION_HEADER]: VERSION,
86
+ ...lease !== null && { [LEASE_HEADER]: lease }
87
+ }
88
+ });
89
+ const admitAttachFrame = (h, registration, frame) => {
90
+ if (frame.ctx.length === 0 && frame.cmd.length === 0) return;
91
+ const admission = admitFrame(h, registration.rec, frame);
92
+ if (admission.type === "refused") sendTo(h, registration.conn, JSON.stringify(admission.syn));
93
+ };
94
+ const getStream = async (h, request) => {
95
+ const refused = await authorize(h, headersOf(request));
96
+ if (refused !== null) return refused;
97
+ const client = clientIdResponse(request);
98
+ if ("response" in client) return client.response;
99
+ const attachment = attachSse(h, client.clientId);
100
+ const lease = attachment.registration === null ? null : mintLease();
101
+ if (attachment.registration !== null) attachment.registration.rec.lease = lease;
102
+ return streamResponse(attachment, lease);
103
+ };
104
+ const postStream = async (h, request) => {
105
+ const client = clientIdResponse(request);
106
+ if ("response" in client) return client.response;
107
+ let body;
108
+ try {
109
+ body = JSON.parse(await request.text());
110
+ } catch {
111
+ return Response.json({ detail: "body must be JSON" }, { status: 400 });
112
+ }
113
+ let frame;
114
+ try {
115
+ frame = parseFrameValue(body, true);
116
+ } catch (error) {
117
+ if (!(error instanceof Error) || error.name !== "StatewireViolation") throw error;
118
+ return Response.json({ detail: error.message }, { status: 400 });
119
+ }
120
+ if (frame.headers === null) return Response.json({ detail: "first frame must carry headers" }, { status: 400 });
121
+ const refused = await authorize(h, mergedHeaders(request, frame.headers));
122
+ if (refused !== null) return refused;
123
+ const attachment = attachSse(h, client.clientId);
124
+ if (attachment.registration === null) return streamResponse(attachment, null);
125
+ const lease = mintLease();
126
+ attachment.registration.rec.lease = lease;
127
+ admitAttachFrame(h, attachment.registration, frame);
128
+ return streamResponse(attachment, lease);
129
+ };
130
+ const stream = async (h, request) => {
131
+ if (request.method === "GET") return getStream(h, request);
132
+ if (request.method === "POST") return postStream(h, request);
133
+ return new Response(null, {
134
+ status: 405,
135
+ headers: { Allow: "GET, POST" }
136
+ });
137
+ };
138
+ const finClient = (h, clientId, fin) => {
139
+ flush(h);
140
+ for (const conn of [...h.conns]) {
141
+ if (conn.clientId !== clientId) continue;
142
+ sendTo(h, conn, JSON.stringify({ fin }));
143
+ closeConn(h, conn);
144
+ }
145
+ };
146
+ const violationResponse = (h, clientId, reason) => {
147
+ finClient(h, clientId, {
148
+ reason: "error",
149
+ message: `protocol violation: ${reason}`
150
+ });
151
+ return Response.json({ error: reason }, { status: 400 });
152
+ };
153
+ const frames = async (h, request) => {
154
+ const refused = await authorize(h, headersOf(request));
155
+ if (refused !== null) return refused;
156
+ const client = clientIdResponse(request);
157
+ if ("response" in client) return client.response;
158
+ const rec = h.clients.get(client.clientId);
159
+ const lease = request.headers.get(LEASE_HEADER);
160
+ if (h.disposed || h.finished !== null || rec === void 0 || lease === null || lease !== rec.lease) return Response.json({ error: "stale-lease" }, { status: 423 });
161
+ let frame;
162
+ try {
163
+ frame = parseFrame(await request.text(), false);
164
+ } catch (error) {
165
+ if (!(error instanceof Error) || error.name !== "StatewireViolation") throw error;
166
+ return violationResponse(h, client.clientId, error.message);
167
+ }
168
+ const admission = admitFrame(h, rec, frame);
169
+ if (admission.type === "refused") return Response.json(admission.syn, { status: 409 });
170
+ return Response.json({});
171
+ };
172
+ //#endregion
173
+ export { frames, stream };
174
+
175
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","names":["StatewireHost","CLIENT_ID_RE","admitFrame","closeConn","flush","parseFrame","parseFrameValue","register","sendTo","ParsedFrame","Registration","SocketHostState","CLIENT_ID_HEADER","LEASE_HEADER","VERSION_HEADER","VERSION","encoder","TextEncoder","invalidClientIdReason","clientId","length","test","clientIdResponse","request","Request","response","Response","headers","get","json","detail","status","reason","headersOf","Record","Object","fromEntries","mergedHeaders","extra","name","value","entries","toLowerCase","authorize","h","Promise","error","mintLease","bytes","crypto","getRandomValues","Uint8Array","btoa","String","fromCharCode","replaceAll","replace","SseAttachment","body","ReadableStream","registration","attachSse","controller","ReadableStreamDefaultController","connection","open","start","next","cancel","close","socket","send","data","frame","enqueue","encode","streamResponse","attachment","lease","admitAttachFrame","ctx","cmd","admission","rec","type","conn","JSON","stringify","syn","getStream","refused","client","postStream","parse","text","Error","message","stream","method","Allow","finClient","fin","Fin","conns","violationResponse","frames","clients","disposed","finished","undefined"],"sources":["../../src/host/http.ts"],"sourcesContent":["import type { StatewireHost } from \"../host\";\nimport {\n CLIENT_ID_RE,\n admitFrame,\n closeConn,\n flush,\n parseFrame,\n parseFrameValue,\n register,\n sendTo,\n} from \"./socket\";\nimport type { ParsedFrame, Registration, SocketHostState } from \"./socket\";\n\nconst CLIENT_ID_HEADER = \"Statewire-Client-Id\";\nconst LEASE_HEADER = \"Statewire-Lease\";\nconst VERSION_HEADER = \"Statewire-Version\";\nconst VERSION = \"1\";\nconst encoder = new TextEncoder();\n\nconst invalidClientIdReason = (clientId: string): string | null => {\n if (clientId === \"\") return \"id must be non-empty\";\n if (clientId.length > 256) return \"id must be at most 256 characters\";\n if (!CLIENT_ID_RE.test(clientId)) {\n return \"id must contain only [A-Za-z0-9._~-]\";\n }\n return null;\n};\n\nconst clientIdResponse = (\n request: Request,\n): { clientId: string } | { response: Response } => {\n const clientId = request.headers.get(CLIENT_ID_HEADER);\n if (clientId === null) {\n return {\n response: Response.json(\n { detail: `missing ${CLIENT_ID_HEADER} header` },\n { status: 400 },\n ),\n };\n }\n const reason = invalidClientIdReason(clientId);\n if (reason !== null) {\n return {\n response: Response.json(\n { detail: `invalid ${CLIENT_ID_HEADER}: ${reason}` },\n { status: 400 },\n ),\n };\n }\n return { clientId };\n};\n\nconst headersOf = (request: Request): Record<string, string> =>\n Object.fromEntries(request.headers);\n\nconst mergedHeaders = (\n request: Request,\n extra: Record<string, string>,\n): Record<string, string> => {\n const headers = headersOf(request);\n for (const [name, value] of Object.entries(extra)) {\n headers[name.toLowerCase()] = value;\n }\n return headers;\n};\n\nconst authorize = async (\n h: SocketHostState,\n headers: Record<string, string>,\n): Promise<Response | null> => {\n try {\n await h.authorize?.({ headers });\n return null;\n } catch (error) {\n if (error instanceof Response) return error;\n throw error;\n }\n};\n\nconst mintLease = (): string => {\n const bytes = crypto.getRandomValues(new Uint8Array(16));\n return btoa(String.fromCharCode(...bytes))\n .replaceAll(\"+\", \"-\")\n .replaceAll(\"/\", \"_\")\n .replace(/=+$/, \"\");\n};\n\ntype SseAttachment = {\n body: ReadableStream<Uint8Array>;\n registration: Registration | null;\n};\n\nconst attachSse = (h: SocketHostState, clientId: string): SseAttachment => {\n let controller: ReadableStreamDefaultController<Uint8Array>;\n let connection: Registration[\"connection\"] | null = null;\n let open = true;\n const body = new ReadableStream({\n start: (next) => {\n controller = next;\n },\n cancel: () => {\n open = false;\n connection?.close();\n },\n });\n const socket = {\n send: (data: string) => {\n if (!open) return;\n const frame = data === \"{}\" ? \": ping\\n\\n\" : `data: ${data}\\n\\n`;\n try {\n controller.enqueue(encoder.encode(frame));\n } catch {\n open = false;\n connection?.close();\n }\n },\n close: () => {\n if (!open) return;\n open = false;\n try {\n controller.close();\n } catch {\n return;\n }\n },\n };\n const registration = register(h, clientId, socket);\n connection = registration?.connection ?? null;\n if (!open) connection?.close();\n return { body, registration };\n};\n\nconst streamResponse = (\n attachment: SseAttachment,\n lease: string | null,\n): Response =>\n new Response(attachment.body, {\n status: 200,\n headers: {\n \"Content-Type\": \"text/event-stream\",\n [VERSION_HEADER]: VERSION,\n ...(lease !== null && { [LEASE_HEADER]: lease }),\n },\n });\n\nconst admitAttachFrame = (\n h: SocketHostState,\n registration: Registration,\n frame: ParsedFrame,\n) => {\n if (frame.ctx.length === 0 && frame.cmd.length === 0) return;\n const admission = admitFrame(h, registration.rec, frame);\n if (admission.type === \"refused\") {\n sendTo(h, registration.conn, JSON.stringify(admission.syn));\n }\n};\n\nconst getStream = async (\n h: SocketHostState,\n request: Request,\n): Promise<Response> => {\n const refused = await authorize(h, headersOf(request));\n if (refused !== null) return refused;\n const client = clientIdResponse(request);\n if (\"response\" in client) return client.response;\n const attachment = attachSse(h, client.clientId);\n const lease = attachment.registration === null ? null : mintLease();\n if (attachment.registration !== null)\n attachment.registration.rec.lease = lease;\n return streamResponse(attachment, lease);\n};\n\nconst postStream = async (\n h: SocketHostState,\n request: Request,\n): Promise<Response> => {\n const client = clientIdResponse(request);\n if (\"response\" in client) return client.response;\n let body: unknown;\n try {\n body = JSON.parse(await request.text());\n } catch {\n return Response.json({ detail: \"body must be JSON\" }, { status: 400 });\n }\n let frame: ParsedFrame;\n try {\n frame = parseFrameValue(body, true);\n } catch (error) {\n if (!(error instanceof Error) || error.name !== \"StatewireViolation\") {\n throw error;\n }\n return Response.json({ detail: error.message }, { status: 400 });\n }\n if (frame.headers === null) {\n return Response.json(\n { detail: \"first frame must carry headers\" },\n { status: 400 },\n );\n }\n const refused = await authorize(h, mergedHeaders(request, frame.headers));\n if (refused !== null) return refused;\n const attachment = attachSse(h, client.clientId);\n if (attachment.registration === null) return streamResponse(attachment, null);\n const lease = mintLease();\n attachment.registration.rec.lease = lease;\n admitAttachFrame(h, attachment.registration, frame);\n return streamResponse(attachment, lease);\n};\n\nexport const stream = async (\n h: SocketHostState,\n request: Request,\n): Promise<Response> => {\n if (request.method === \"GET\") return getStream(h, request);\n if (request.method === \"POST\") return postStream(h, request);\n return new Response(null, {\n status: 405,\n headers: { Allow: \"GET, POST\" },\n });\n};\n\nconst finClient = (\n h: SocketHostState,\n clientId: string,\n fin: StatewireHost.Fin,\n) => {\n flush(h);\n for (const conn of [...h.conns]) {\n if (conn.clientId !== clientId) continue;\n sendTo(h, conn, JSON.stringify({ fin }));\n closeConn(h, conn);\n }\n};\n\nconst violationResponse = (\n h: SocketHostState,\n clientId: string,\n reason: string,\n) => {\n finClient(h, clientId, {\n reason: \"error\",\n message: `protocol violation: ${reason}`,\n });\n return Response.json({ error: reason }, { status: 400 });\n};\n\nexport const frames = async (\n h: SocketHostState,\n request: Request,\n): Promise<Response> => {\n const refused = await authorize(h, headersOf(request));\n if (refused !== null) return refused;\n const client = clientIdResponse(request);\n if (\"response\" in client) return client.response;\n const rec = h.clients.get(client.clientId);\n const lease = request.headers.get(LEASE_HEADER);\n if (\n h.disposed ||\n h.finished !== null ||\n rec === undefined ||\n lease === null ||\n lease !== rec.lease\n ) {\n return Response.json({ error: \"stale-lease\" }, { status: 423 });\n }\n let frame: ParsedFrame;\n try {\n frame = parseFrame(await request.text(), false);\n } catch (error) {\n if (!(error instanceof Error) || error.name !== \"StatewireViolation\") {\n throw error;\n }\n return violationResponse(h, client.clientId, error.message);\n }\n const admission = admitFrame(h, rec, frame);\n if (admission.type === \"refused\") {\n return Response.json(admission.syn, { status: 409 });\n }\n return Response.json({});\n};\n"],"mappings":"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"}