statewire 0.11.0 → 0.12.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/README.md +2 -2
- package/dist/client/connection.d.ts +6 -4
- package/dist/client/connection.d.ts.map +1 -1
- package/dist/client/connection.js +10 -2
- package/dist/client/connection.js.map +1 -1
- package/dist/client/core.d.ts +1 -1
- package/dist/client/session.d.ts +4 -2
- package/dist/client/session.d.ts.map +1 -1
- package/dist/client/session.js +102 -102
- package/dist/client/session.js.map +1 -1
- package/dist/host/draft.d.ts.map +1 -1
- package/dist/host/draft.js +9 -1
- package/dist/host/draft.js.map +1 -1
- package/dist/host/host.d.ts +1 -1
- package/dist/host/http.js +4 -3
- package/dist/host/http.js.map +1 -1
- package/dist/host/socket.d.ts +6 -5
- package/dist/host/socket.d.ts.map +1 -1
- package/dist/host/socket.js +12 -20
- package/dist/host/socket.js.map +1 -1
- package/dist/host-internal.d.ts +2 -1
- package/dist/host-internal.d.ts.map +1 -1
- package/dist/host.d.ts +2 -2
- package/dist/host.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/statewire.d.ts +15 -4
- package/dist/statewire.d.ts.map +1 -1
- package/dist/statewire.js.map +1 -1
- package/dist/transports/fake-sse.js +4 -4
- package/dist/transports/fake-sse.js.map +1 -1
- package/dist/transports/fixtures.d.ts +1 -1
- package/dist/transports/remote.d.ts +1 -1
- package/dist/transports/remote.js +1 -0
- package/dist/transports/remote.js.map +1 -1
- package/dist/transports/sse.d.ts +2 -2
- package/dist/transports/testing.d.ts +1 -1
- package/dist/transports/testing.js +2 -4
- package/dist/transports/testing.js.map +1 -1
- package/dist/wire/meta.d.ts +5 -3
- package/dist/wire/meta.d.ts.map +1 -1
- package/dist/wire/meta.js +7 -4
- package/dist/wire/meta.js.map +1 -1
- package/dist/wire/ops.d.ts.map +1 -1
- package/dist/wire/ops.js +26 -2
- package/dist/wire/ops.js.map +1 -1
- package/package.json +1 -1
- package/src/client/connection.ts +12 -6
- package/src/client/session.ts +145 -139
- package/src/host/draft.ts +15 -1
- package/src/host/http.ts +10 -9
- package/src/host/socket.ts +18 -28
- package/src/host-internal.ts +2 -1
- package/src/host.ts +0 -1
- package/src/statewire.ts +18 -4
- package/src/transports/fake-sse.ts +6 -6
- package/src/transports/remote.ts +1 -0
- package/src/transports/testing.ts +2 -7
- package/src/wire/meta.ts +12 -4
- package/src/wire/ops.ts +30 -3
|
@@ -1 +1 @@
|
|
|
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","Shared","txq","parked","SessionVerdict","unsubscribe","Session","record","SessionRecord","ctx","recordPersisted","store","shared","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","mergeBacklog","own","known","Set","foreign","c","has","sort","a","b","MAX_PARKED_VERDICTS","syncBacklogInner","splice","parkedSeqs","v","verdicts","length","slice","unchanged","every","i","undefined","syncBacklog","transact","fn","run","then","lock","stored","load","clientId","adoptReset","setIdle","level","cover","entry","onApplied","onCovered","settleEntry","outcome","ok","onSettle","type","settleAll","detach","cause","unmount","gone","superseded","const","batch","detachFailure","predecessorLost","some","fate","restateContext","ctxNextSeq","resetRecord","survivors","nextSeq","old","enqueueInner","pump","ephemeralDetach","rejectedFailure","cmd","code","outcomeOf","String","TERMINAL_CMD_TYPES","parkVerdict","handleCmd","Number","isSafeInteger","dur","Math","max","find","consumeVerdicts","consumed","verdict","add","size","handleCmds","cmds","ingestOps","state","changedPaths","publishIngested","RECORDS","snapClocks","seqWm","ctxWm","tail","cmdAligned","ctxAligned","handleHello","syn","includes","attachCmd","attachCtx","reconcile","adoptStored","ctxWatermark","ctxSeq","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","subscribe","stop","setTransport","ts","onStart","enqueue","sendCommand","encoded","setContext","value","Array","isArray","op","path","mint","useStatewireSession","options","defaultStore","mounted","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 Shared = {\n txq: Promise<void>;\n parked: StatewireClient.SessionVerdict[];\n unsubscribe: (() => void) | null;\n};\n\ntype Session = {\n record: StatewireClient.SessionRecord;\n ctx: StatewireRemoteProps;\n recordPersisted: boolean;\n store: StatewireClient.SessionStorage;\n shared: Shared | false;\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\nconst mergeBacklog = (s: Session): StatewireClient.SessionCommand[] => {\n const own = backlog(s);\n if (!s.shared) return own;\n const known = new Set(s.pending.map((p) => p.seq));\n const foreign = s.record.commands.filter(\n (c) => !known.has(c.seq) && c.seq > s.durableSeq,\n );\n return [...foreign, ...own].sort((a, b) => a.seq - b.seq);\n};\n\nconst MAX_PARKED_VERDICTS = 64;\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 syncBacklogInner = (s: Session) => {\n const parked = s.shared ? s.shared.parked.splice(0) : [];\n const parkedSeqs = new Set(parked.map((v) => v.seq));\n const commands = mergeBacklog(s).filter((c) => !parkedSeqs.has(c.seq));\n const verdicts =\n parked.length > 0\n ? [...(s.record.verdicts ?? []), ...parked].slice(-MAX_PARKED_VERDICTS)\n : s.record.verdicts;\n const unchanged =\n verdicts === s.record.verdicts &&\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, {\n ...s.record,\n commands,\n ...(verdicts !== undefined && { verdicts }),\n });\n};\n\nconst syncBacklog = (s: Session) => {\n if (!s.shared) return syncBacklogInner(s);\n void transact(s, () => syncBacklogInner(s));\n};\n\n// Every record mutation over shared storage is a locked read-modify-write.\nconst transact = (s: Session, fn: () => void): Promise<void> => {\n if (!s.shared || s.deactivated) {\n fn();\n return Promise.resolve();\n }\n const shared = s.shared;\n const run = shared.txq.then(() =>\n s.store.lock!(async () => {\n const stored = s.store.load();\n if (stored !== null && stored.clientId !== s.record.clientId) {\n adoptReset(s, stored);\n } else if (stored !== null) {\n s.record = stored;\n }\n fn();\n }),\n );\n shared.txq = run.then(\n () => {},\n () => {},\n );\n return run;\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 enqueueInner(s, old.command, old.inv.params, old.resolve, old.reject);\n }\n restateContext(s);\n publish(s);\n wake(s);\n void pump(s);\n};\n\n// Another sharer replaced the record's identity: their reset settles our\n// attempted history and our survivors re-stamp on the stored clocks.\nconst adoptReset = (s: Session, stored: StatewireClient.SessionRecord) => {\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 s.record = stored;\n for (const old of survivors) {\n s.lifecycle?.onCovered?.(old.inv);\n enqueueInner(s, old.command, old.inv.params, old.resolve, old.reject);\n }\n restateContext(s);\n publish(s);\n wake(s);\n void pump(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 outcomeOf = (\n cmd: StatewireCmd,\n):\n | { ok: true; payload: unknown }\n | { ok: false; failure: StatewireClient.SendFailure } =>\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\nconst TERMINAL_CMD_TYPES = new Set([\n \"result\",\n \"rejected\",\n \"failed\",\n \"crashed\",\n \"expired\",\n]);\n\nconst parkVerdict = (s: Session, cmd: StatewireCmd) => {\n if (!s.shared || !TERMINAL_CMD_TYPES.has(cmd.type!)) return;\n s.shared.parked.push({\n seq: cmd.seq,\n type: cmd.type as StatewireClient.SessionVerdict[\"type\"],\n ...(cmd.code !== undefined && { code: cmd.code }),\n ...(cmd.message !== undefined && { message: cmd.message }),\n ...(cmd.payload !== undefined && { payload: cmd.payload }),\n ...(cmd.meta !== undefined && { meta: cmd.meta }),\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 parkVerdict(s, cmd);\n if (!entry) return;\n cover(s, entry);\n settleEntry(s, entry, outcomeOf(cmd));\n};\n\nconst consumeVerdicts = (s: Session) => {\n const verdicts = s.record.verdicts ?? [];\n const consumed = new Set<number>();\n for (const verdict of verdicts) {\n const entry = s.pending.find((p) => p.seq === verdict.seq);\n if (!entry) continue;\n consumed.add(verdict.seq);\n cover(s, entry);\n settleEntry(s, entry, outcomeOf(verdict as StatewireCmd));\n }\n if (consumed.size === 0) return;\n setRecord(s, {\n ...s.record,\n commands: s.record.commands.filter((c) => !consumed.has(c.seq)),\n verdicts: verdicts.filter((v) => !consumed.has(v.seq)),\n });\n publish(s);\n wake(s);\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 enqueueInner(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 const reconcile = () => {\n if (record === \"resumed\") {\n if (s.shared) adoptStored(s);\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 syncBacklogInner(s);\n } else {\n resetRecord(s, s.record.clientId);\n }\n s.sessionKnown = true;\n };\n if (!s.shared || s.deactivated) {\n reconcile();\n publishIngested(s);\n void pump(s);\n return;\n }\n void transact(s, reconcile).then(() => {\n publishIngested(s);\n void pump(s);\n });\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 // Superseded over shared storage: the superseder resends our backlog and\n // parks the verdicts; our promises settle from the observed record.\n if (!(s.shared && reason === \"superseded\")) {\n settleAll(s, reason === \"superseded\" ? \"superseded\" : \"gone\");\n }\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 // Shared storage: the lock across the send keeps per-lane arrival\n // ordered across sharers during attach handover.\n const send = () => transport.send(lanes.frame);\n result = await (s.shared && !s.deactivated\n ? s.store.lock!(send)\n : send());\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.shared) {\n s.shared.unsubscribe = s.store.subscribe!(() => {\n void transact(s, () => consumeVerdicts(s));\n });\n s.running = true;\n void transact(s, () => {\n if (s.store.load() === null) s.store.save(s.record);\n s.recordPersisted = true;\n adoptStored(s);\n consumeVerdicts(s);\n }).then(() => void pump(s));\n return;\n }\n if (!s.recordPersisted) {\n s.recordPersisted = true;\n s.store.save(s.record);\n }\n adoptStored(s);\n s.running = true;\n void pump(s);\n};\n\nconst stop = (s: Session) => {\n s.running = false;\n if (s.shared) {\n s.shared.unsubscribe?.();\n s.shared.unsubscribe = null;\n }\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 enqueueInner = (\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: mergeBacklog(s) });\n s.lifecycle?.onStart(entry.inv);\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 if (!s.shared) {\n enqueueInner(s, command, params, resolve, reject);\n void pump(s);\n return;\n }\n void transact(s, () =>\n enqueueInner(s, command, params, resolve, reject),\n ).then(() => 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 adoptStored = (s: Session) => {\n const known = new Set(s.pending.map((p) => p.seq));\n const stored = s.record.commands.filter((c) => !known.has(c.seq));\n if (stored.length === 0) return;\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 s.pending.sort((a, b) => a.seq - b.seq);\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 if (!s.running || isFinished(s)) return;\n const { value: encoded, meta } = encodeValue(value);\n const ops = [\n {\n op: \"replace\",\n path: [],\n value: encoded,\n ...(meta !== undefined && { meta }),\n },\n ];\n s.ctxOps = ops;\n const mint = () => {\n const seq = s.record.ctxNextSeq;\n setRecord(s, { ...s.record, ctxNextSeq: seq + 1 });\n s.ctxPending.push({ seq, ops, posted: false });\n };\n if (!s.shared) {\n mint();\n wake(s);\n void pump(s);\n return;\n }\n void transact(s, mint).then(() => {\n wake(s);\n void pump(s);\n });\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 if ((store.lock === undefined) !== (store.subscribe === undefined)) {\n throw new Error(\n \"statewire: session storage must provide lock and subscribe together\",\n );\n }\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 shared:\n store.lock !== undefined\n ? { txq: Promise.resolve(), parked: [], unsubscribe: null }\n : false,\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
|
+
{"version":3,"file":"session.js","names":["useContext","useEffect","useState","applyOps","StatewirePath","TERMINAL","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","onSessionLost","OnSessionLost","onDone","Shared","txq","parked","SessionVerdict","unsubscribe","Session","record","SessionRecord","ctx","recordPersisted","store","shared","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","mergeBacklog","own","known","Set","foreign","c","has","sort","a","b","MAX_PARKED_VERDICTS","syncBacklogInner","splice","parkedSeqs","v","verdicts","length","slice","unchanged","every","i","undefined","syncBacklog","transact","fn","run","then","lock","stored","load","clientId","adoptReset","setIdle","level","cover","entry","onApplied","onCovered","settleEntry","outcome","ok","onSettle","type","settleAll","detach","cause","const","batch","detachFailure","predecessorLost","some","fate","restateContext","ctxNextSeq","settleLost","entries","rebaseRecord","adopt","held","survivors","old","enqueueInner","pump","resetRecord","nextSeq","ephemeralDetach","rejectedFailure","cmd","code","outcomeOf","String","TERMINAL_CMD_TYPES","parkVerdict","handleCmd","Number","isSafeInteger","dur","Math","max","find","consumeVerdicts","consumed","verdict","add","size","handleCmds","cmds","ingestOps","state","changedPaths","publishIngested","snapCtxClock","ctxWm","ctxAligned","sessionLostInner","seqWm","handleHello","syn","attachCmd","attachCtx","reconcile","adoptStored","ctxWatermark","ctxSeq","handleOps","handleRefusal","watermark","retry","Date","now","firstCmd","firstCtx","cmdGap","ctxGap","handleDetach","handleFinish","reason","isFinished","Lanes","frame","cmdEntries","ctxEntries","buildLanes","at","takeAttachLanes","wanted","lanes","connection","status","reconnect","resumeDelay","epoch","send","includes","onPosted","start","subscribe","stop","setTransport","ts","onStart","enqueue","sendCommand","encoded","setContext","value","Array","isArray","op","path","mint","useStatewireSession","options","defaultStore","mounted","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 { TERMINAL } from \"./connection\";\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 onSessionLost?: StatewireClient.OnSessionLost | undefined;\n onDone?: (() => void) | undefined;\n};\n\ntype Shared = {\n txq: Promise<void>;\n parked: StatewireClient.SessionVerdict[];\n unsubscribe: (() => void) | null;\n};\n\ntype Session = {\n record: StatewireClient.SessionRecord;\n ctx: StatewireRemoteProps;\n recordPersisted: boolean;\n store: StatewireClient.SessionStorage;\n shared: Shared | false;\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 onSessionLost: StatewireClient.OnSessionLost | undefined;\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\nconst mergeBacklog = (s: Session): StatewireClient.SessionCommand[] => {\n const own = backlog(s);\n if (!s.shared) return own;\n const known = new Set(s.pending.map((p) => p.seq));\n const foreign = s.record.commands.filter(\n (c) => !known.has(c.seq) && c.seq > s.durableSeq,\n );\n return [...foreign, ...own].sort((a, b) => a.seq - b.seq);\n};\n\nconst MAX_PARKED_VERDICTS = 64;\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 syncBacklogInner = (s: Session) => {\n const parked = s.shared ? s.shared.parked.splice(0) : [];\n const parkedSeqs = new Set(parked.map((v) => v.seq));\n const commands = mergeBacklog(s).filter((c) => !parkedSeqs.has(c.seq));\n const verdicts =\n parked.length > 0\n ? [...(s.record.verdicts ?? []), ...parked].slice(-MAX_PARKED_VERDICTS)\n : s.record.verdicts;\n const unchanged =\n verdicts === s.record.verdicts &&\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, {\n ...s.record,\n commands,\n ...(verdicts !== undefined && { verdicts }),\n });\n};\n\nconst syncBacklog = (s: Session) => {\n if (!s.shared) return syncBacklogInner(s);\n void transact(s, () => syncBacklogInner(s));\n};\n\n// Every record mutation over shared storage is a locked read-modify-write.\nconst transact = (s: Session, fn: () => void): Promise<void> => {\n if (!s.shared || s.deactivated) {\n fn();\n return Promise.resolve();\n }\n const shared = s.shared;\n const run = shared.txq.then(() =>\n s.store.lock!(async () => {\n const stored = s.store.load();\n if (stored !== null && stored.clientId !== s.record.clientId) {\n adoptReset(s, stored);\n } else if (stored !== null) {\n s.record = stored;\n }\n fn();\n }),\n );\n shared.txq = run.then(\n () => {},\n () => {},\n );\n return run;\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\") => {\n const cause =\n detach === \"unmount\" ? (\"unmount\" as const) : (\"stream-gone\" as const);\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\nconst settleLost = (s: Session, entries: Pending[]) => {\n for (const entry of entries) {\n s.lifecycle?.onCovered?.(entry.inv);\n settleEntry(s, entry, {\n ok: false,\n failure: { fate: \"lost\", cause: \"session-lost\" },\n });\n }\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// on the adopted lane clocks.\nconst rebaseRecord = (s: Session, adopt: () => void) => {\n const held = [...s.pending];\n const survivors = held.filter((p) => !p.attempted && !p.applied);\n settleLost(\n s,\n held.filter((p) => p.attempted || p.applied),\n );\n s.pending = [];\n s.durableSeq = 0;\n adopt();\n for (const old of survivors) {\n s.lifecycle?.onCovered?.(old.inv);\n enqueueInner(s, old.command, old.inv.params, old.resolve, old.reject);\n }\n restateContext(s);\n publish(s);\n wake(s);\n void pump(s);\n};\n\nconst resetRecord = (s: Session, clientId: string) =>\n rebaseRecord(s, () =>\n setRecord(s, { clientId, nextSeq: 1, ctxNextSeq: 1, commands: [] }),\n );\n\n// Another sharer replaced the record's identity: their reset settles our\n// attempted history and our survivors re-stamp on the stored clocks.\nconst adoptReset = (s: Session, stored: StatewireClient.SessionRecord) =>\n rebaseRecord(s, () => {\n s.record = stored;\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 outcomeOf = (\n cmd: StatewireCmd,\n):\n | { ok: true; payload: unknown }\n | { ok: false; failure: StatewireClient.SendFailure } => {\n switch (cmd.type) {\n case \"result\":\n return { ok: true, payload: reviveResponse(cmd).payload };\n case \"rejected\":\n return { ok: false, failure: rejectedFailure(reviveResponse(cmd)) };\n case \"failed\":\n return {\n ok: false,\n failure: {\n fate: \"failed\",\n cause: \"server\",\n ...(cmd.message !== undefined && { message: cmd.message }),\n },\n };\n case \"crashed\":\n return { ok: false, failure: { fate: \"crashed\", cause: \"server\" } };\n case \"expired\":\n return { ok: false, failure: { fate: \"expired\", cause: \"server\" } };\n default:\n return protocolError(`unknown cmd type: ${String(cmd.type)}`);\n }\n};\n\nconst TERMINAL_CMD_TYPES: ReadonlySet<string> = new Set(TERMINAL);\n\nconst parkVerdict = (s: Session, cmd: StatewireCmd) => {\n if (!s.shared || !TERMINAL_CMD_TYPES.has(cmd.type!)) return;\n s.shared.parked.push({\n seq: cmd.seq,\n type: cmd.type as StatewireClient.SessionVerdict[\"type\"],\n ...(cmd.code !== undefined && { code: cmd.code }),\n ...(cmd.message !== undefined && { message: cmd.message }),\n ...(cmd.payload !== undefined && { payload: cmd.payload }),\n ...(cmd.meta !== undefined && { meta: cmd.meta }),\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 parkVerdict(s, cmd);\n if (!entry) return;\n cover(s, entry);\n settleEntry(s, entry, outcomeOf(cmd));\n};\n\nconst consumeVerdicts = (s: Session) => {\n const verdicts = s.record.verdicts ?? [];\n const consumed = new Set<number>();\n for (const verdict of verdicts) {\n const entry = s.pending.find((p) => p.seq === verdict.seq);\n if (!entry) continue;\n consumed.add(verdict.seq);\n cover(s, entry);\n settleEntry(s, entry, outcomeOf(verdict as StatewireCmd));\n }\n if (consumed.size === 0) return;\n setRecord(s, {\n ...s.record,\n commands: s.record.commands.filter((c) => !consumed.has(c.seq)),\n verdicts: verdicts.filter((v) => !consumed.has(v.seq)),\n });\n publish(s);\n wake(s);\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\n// A hello's cmd watermark may understate a durable record (cold host, tray\n// unloaded): held seqs resend as-is and the frame's answer arbitrates —\n// absorption where the record survived, a gap refusal where it did not.\nconst snapCtxClock = (s: Session, ctxWm: number) => {\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\n// The refusal's watermarks are exact: every held statement above them was\n// never admitted by the live record, and the first unposted seq being beyond\n// watermark+1 proves the record that admitted the missing prefix is gone —\n// its history settles lost and the lane clocks restart at the watermarks.\nconst sessionLostInner = (s: Session, seqWm: number, ctxWm: number) => {\n const held = [...s.pending];\n settleLost(s, held);\n s.pending = [];\n s.durableSeq = seqWm;\n setRecord(s, {\n clientId: s.record.clientId,\n nextSeq: seqWm + 1,\n ctxNextSeq: ctxWm + 1,\n commands: [],\n });\n restateContext(s);\n if (\n held.length > 0 &&\n s.onSessionLost?.(held.map((p) => p.inv)) === \"requeue\"\n ) {\n for (const old of held) {\n enqueueInner(\n s,\n old.command,\n old.inv.params,\n () => {},\n () => {},\n );\n }\n }\n publish(s);\n wake(s);\n};\n\nconst handleHello = (\n s: Session,\n { ops, syn }: { ops: unknown[]; syn: StatewireSyn },\n) => {\n if (syn.record !== undefined && syn.record !== \"ephemeral\") {\n protocolError(`unknown record: ${String(syn.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 = syn.record === \"ephemeral\";\n if (s.ephemeral) s.deactivated = true;\n const [attachCmd, attachCtx] = s.attachLaneSeqs;\n s.attachLaneSeqs = [new Set(), new Set()];\n const reconcile = () => {\n if (s.shared) adoptStored(s);\n // Resending unsettled statements is exactly-once-safe: absorbed replays\n // re-emit their stored verdicts (applied while still in flight).\n s.durableSeq = syn.seq ?? 0;\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 snapCtxClock(s, ctxWatermark);\n syncBacklogInner(s);\n s.sessionKnown = true;\n };\n if (!s.shared || s.deactivated) {\n reconcile();\n publishIngested(s);\n void pump(s);\n return;\n }\n void transact(s, reconcile).then(() => {\n publishIngested(s);\n void pump(s);\n });\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 retry-less {syn} refusal is the one session-loss verdict: a cmd gap\n// against its exact watermark settles the held history lost; a pushback\n// (retry present) or a gapless refusal only reconciles the resend queues.\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 wake(s);\n void pump(s);\n return;\n }\n const firstCmd = s.pending.find((p) => !p.posted);\n const firstCtx = s.ctxPending.find((c) => !c.posted);\n const cmdGap =\n typeof seq === \"number\" &&\n firstCmd !== undefined &&\n firstCmd.seq > Math.max(seq, s.durableSeq) + 1;\n const ctxGap =\n typeof ctxSeq === \"number\" &&\n firstCtx !== undefined &&\n firstCtx.seq > ctxSeq + 1;\n if (cmdGap || ctxGap) {\n void transact(s, () => {\n if (cmdGap) sessionLostInner(s, seq!, ctxSeq ?? 0);\n else snapCtxClock(s, ctxSeq!);\n }).then(() => {\n wake(s);\n void pump(s);\n });\n return;\n }\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, \"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 // Shared storage: the lock across the send keeps per-lane arrival\n // ordered across sharers during attach handover.\n const send = () => transport.send(lanes.frame);\n result = await (s.shared && !s.deactivated\n ? s.store.lock!(send)\n : send());\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.shared) {\n s.shared.unsubscribe = s.store.subscribe!(() => {\n void transact(s, () => consumeVerdicts(s));\n });\n s.running = true;\n void transact(s, () => {\n if (s.store.load() === null) s.store.save(s.record);\n s.recordPersisted = true;\n adoptStored(s);\n consumeVerdicts(s);\n }).then(() => void pump(s));\n return;\n }\n if (!s.recordPersisted) {\n s.recordPersisted = true;\n s.store.save(s.record);\n }\n adoptStored(s);\n s.running = true;\n void pump(s);\n};\n\nconst stop = (s: Session) => {\n s.running = false;\n if (s.shared) {\n s.shared.unsubscribe?.();\n s.shared.unsubscribe = null;\n }\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 enqueueInner = (\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: mergeBacklog(s) });\n s.lifecycle?.onStart(entry.inv);\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 if (!s.shared) {\n enqueueInner(s, command, params, resolve, reject);\n void pump(s);\n return;\n }\n void transact(s, () =>\n enqueueInner(s, command, params, resolve, reject),\n ).then(() => void pump(s));\n};\n\n// A stored command may have reached a server: attempted settles it lost,\n// never aborted, when a channel death ends the session.\nconst adoptStored = (s: Session) => {\n const known = new Set(s.pending.map((p) => p.seq));\n const stored = s.record.commands.filter((c) => !known.has(c.seq));\n if (stored.length === 0) return;\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 s.pending.sort((a, b) => a.seq - b.seq);\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 if (!s.running || isFinished(s)) return;\n const { value: encoded, meta } = encodeValue(value);\n const ops = [\n {\n op: \"replace\",\n path: [],\n value: encoded,\n ...(meta !== undefined && { meta }),\n },\n ];\n s.ctxOps = ops;\n const mint = () => {\n const seq = s.record.ctxNextSeq;\n setRecord(s, { ...s.record, ctxNextSeq: seq + 1 });\n s.ctxPending.push({ seq, ops, posted: false });\n };\n if (!s.shared) {\n mint();\n wake(s);\n void pump(s);\n return;\n }\n void transact(s, mint).then(() => {\n wake(s);\n void pump(s);\n });\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 if ((store.lock === undefined) !== (store.subscribe === undefined)) {\n throw new Error(\n \"statewire: session storage must provide lock and subscribe together\",\n );\n }\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 shared:\n store.lock !== undefined\n ? { txq: Promise.resolve(), parked: [], unsubscribe: null }\n : false,\n ephemeral: false,\n deactivated: false,\n sessionKnown: false,\n durableSeq: 0,\n resumeAt: 0,\n idle: false,\n onSessionLost: undefined,\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 if ((store.lock !== undefined) !== (s.shared !== false)) {\n throw new Error(\n \"statewire: session storage changed sharedness across renders — lock/subscribe must be present from the first render or never\",\n );\n }\n s.store = store;\n s.onSessionLost = options.onSessionLost;\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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"}
|
package/dist/host/draft.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"draft.d.ts","names":[],"sources":["../../src/host/draft.ts"],"mappings":";;;KAMY;EACV;EACA,SAAS,IAAI,UAAU,IAAI,aAAa;;KAG9B,WAAW;EACrB,OAAO;;EAEP;;EAEA;;
|
|
1
|
+
{"version":3,"file":"draft.d.ts","names":[],"sources":["../../src/host/draft.ts"],"mappings":";;;KAMY;EACV;EACA,SAAS,IAAI,UAAU,IAAI,aAAa;;KAG9B,WAAW;EACrB,OAAO;;EAEP;;EAEA;;cAuCW,mBAAoB,GAAG,MAAM,cAAY,WAAW;;;;;;;cA0QpD,SAAU,8BACrB,oBAAoB,KAAK,kBACzB,KAAK,MACL"}
|
package/dist/host/draft.js
CHANGED
|
@@ -5,6 +5,14 @@ import { sanitizeValue } from "../wire/ops.js";
|
|
|
5
5
|
const NODE = Symbol("statewire.draftNode");
|
|
6
6
|
const isIndex = (key) => /^(0|[1-9]\d*)$/.test(key);
|
|
7
7
|
const invalid = (path, message) => /* @__PURE__ */ new Error(`statewire: ${message} at path: ${path.join(".")}`);
|
|
8
|
+
const codePointLength = (text) => {
|
|
9
|
+
let count = 0;
|
|
10
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
11
|
+
if (text.codePointAt(index) > 65535) index += 1;
|
|
12
|
+
count += 1;
|
|
13
|
+
}
|
|
14
|
+
return count;
|
|
15
|
+
};
|
|
8
16
|
const createStateDraft = (host) => {
|
|
9
17
|
let gen = 0;
|
|
10
18
|
const dirty = [];
|
|
@@ -45,7 +53,7 @@ const createStateDraft = (host) => {
|
|
|
45
53
|
path: [
|
|
46
54
|
...node.path,
|
|
47
55
|
key,
|
|
48
|
-
existing
|
|
56
|
+
codePointLength(existing)
|
|
49
57
|
],
|
|
50
58
|
value: suffix
|
|
51
59
|
}, changedPathFor(node, key));
|
package/dist/host/draft.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"draft.js","names":["Statewire","StatewirePath","sanitizeValue","isUnsafeKey","sharedDraftTraps","DraftHost","snapshot","record","op","Op","changedPath","StateDraft","proxy","T","flush","invalidate","NODE","Symbol","NodeHandle","append","key","suffix","isIndex","test","invalid","path","message","Error","join","Container","Record","Node","parent","gen","base","copy","children","Map","isArray","handle","createStateDraft","host","dirty","latest","node","resolve","value","prepareForWrite","Array","slice","push","changedPathFor","applyAppend","rawKey","container","Number","existing","Object","hasOwn","undefined","length","createNode","seed","childKey","current","childProxy","prop","child","get","set","assign","old","isObjectContainer","Proxy","_target","c","next","isInteger","String","i","splice","index","deleteProperty","has","Reflect","root","TKey"],"sources":["../../src/host/draft.ts"],"sourcesContent":["import type { Statewire } from \"../types\";\nimport type { StatewirePath } from \"../wire/ops\";\nimport { sanitizeValue } from \"../wire/ops\";\nimport { isUnsafeKey } from \"../util/statePath\";\nimport { sharedDraftTraps } from \"../util/draftTraps\";\n\nexport type DraftHost = {\n snapshot: () => unknown;\n record: (op: Statewire.Op, changedPath: StatewirePath) => void;\n};\n\nexport type StateDraft<T> = {\n proxy: T;\n /** Seal pending writes and return the committed state. */\n flush: () => unknown;\n /** Committed state was replaced externally; cached nodes re-resolve. */\n invalidate: () => void;\n};\n\nconst NODE = Symbol(\"statewire.draftNode\");\n\ntype NodeHandle = {\n append: (key: string | number, suffix: string) => void;\n};\n\nconst isIndex = (key: string) => /^(0|[1-9]\\d*)$/.test(key);\n\nconst invalid = (path: StatewirePath, message: string) =>\n new Error(`statewire: ${message} at path: ${path.join(\".\")}`);\n\ntype Container = Record<string | number, unknown>;\n\ntype Node = {\n parent: Node | null;\n key: string | number;\n path: StatewirePath;\n gen: number;\n base: unknown;\n copy: Container | null;\n children: Map<string, Node>;\n isArray: boolean;\n proxy: object;\n handle: NodeHandle;\n};\n\nexport const createStateDraft = <T>(host: DraftHost): StateDraft<T> => {\n let gen = 0;\n const dirty: Node[] = [];\n\n const latest = (node: Node): unknown => node.copy ?? node.base;\n\n const resolve = (node: Node): Node => {\n if (node.gen === gen) return node;\n node.gen = gen;\n if (node.parent === null) {\n if (node.copy === null) node.base = host.snapshot();\n return node;\n }\n const value = (latest(resolve(node.parent)) as Container)[node.key];\n if (value !== latest(node)) {\n node.base = value;\n node.copy = null;\n }\n return node;\n };\n\n const prepareForWrite = (node: Node): Container => {\n resolve(node);\n if (node.copy !== null) return node.copy;\n const base = node.base;\n node.copy = Array.isArray(base)\n ? (base.slice() as unknown as Container)\n : { ...(base as Container) };\n dirty.push(node);\n if (node.parent !== null) {\n prepareForWrite(node.parent)[node.key] = node.copy;\n }\n return node.copy;\n };\n\n const changedPathFor = (node: Node, key: string | number): StatewirePath =>\n Array.isArray(latest(node)) ? node.path : [...node.path, key];\n\n const applyAppend = (node: Node, rawKey: string | number, suffix: string) => {\n const container = latest(resolve(node)) as Container;\n const key =\n Array.isArray(container) && typeof rawKey === \"string\" && isIndex(rawKey)\n ? Number(rawKey)\n : rawKey;\n const existing = Object.hasOwn(container as object, key)\n ? container[key]\n : undefined;\n if (typeof existing !== \"string\") {\n throw invalid([...node.path, key], \"cannot append to a non-string\");\n }\n if (suffix === \"\") return;\n prepareForWrite(node)[key] = existing + suffix;\n host.record(\n { op: \"add\", path: [...node.path, key, existing.length], value: suffix },\n changedPathFor(node, key),\n );\n };\n\n const createNode = (parent: Node | null, key: string | number): Node => {\n const seed =\n parent === null ? host.snapshot() : (latest(parent) as Container)[key];\n const node: Node = {\n parent,\n key,\n path: parent === null ? [] : [...parent.path, key],\n gen,\n base: seed,\n copy: null,\n children: new Map(),\n isArray: Array.isArray(seed),\n proxy: null!,\n handle: null!,\n };\n node.handle = {\n append: (childKey, suffix) => applyAppend(node, childKey, suffix),\n };\n\n const current = () => latest(resolve(node));\n\n const childProxy = (\n prop: string,\n childKey: string | number,\n value: object,\n ) => {\n let child = node.children.get(prop);\n if (child === undefined || child.isArray !== Array.isArray(value)) {\n child = createNode(node, childKey);\n node.children.set(prop, child);\n }\n return child.proxy;\n };\n\n const assign = (\n container: Container,\n key: string | number,\n value: unknown,\n ) => {\n const old = Object.hasOwn(container as object, key)\n ? container[key]\n : undefined;\n if (old === value) return;\n const isObjectContainer = !Array.isArray(container);\n if (\n isObjectContainer &&\n isUnsafeKey(key, node.path[node.path.length - 1])\n ) {\n throw invalid([...node.path, key], `cannot set unsafe key \"${key}\"`);\n }\n if (value === undefined) {\n if (!isObjectContainer) {\n throw invalid([...node.path, key], \"cannot serialize undefined\");\n }\n delete prepareForWrite(node)[key];\n host.record({ op: \"remove\", path: [...node.path, key] }, [\n ...node.path,\n key,\n ]);\n gen++;\n return;\n }\n prepareForWrite(node)[key] = isObjectContainer\n ? sanitizeValue(value, key as string)\n : sanitizeValue(value);\n host.record(\n { op: \"replace\", path: [...node.path, key], value },\n changedPathFor(node, key),\n );\n if (\n (typeof old === \"object\" && old !== null) ||\n (typeof value === \"object\" && value !== null)\n ) {\n gen++;\n }\n };\n\n node.proxy = new Proxy(seed as object, {\n get: (_target, prop) => {\n if (prop === NODE) return node.handle;\n const container = current();\n if (typeof prop === \"symbol\") {\n return (container as Record<symbol, unknown>)[prop];\n }\n const c = container as Container;\n const key = Array.isArray(c) && isIndex(prop) ? Number(prop) : prop;\n const value = c[key];\n if (typeof value === \"object\" && value !== null) {\n return childProxy(prop, key, value);\n }\n return value;\n },\n set: (_target, prop, value) => {\n if (typeof prop === \"symbol\") return false;\n const container = current();\n if (Array.isArray(container)) {\n if (prop === \"length\") {\n const next = Number(value);\n if (\n !Number.isInteger(next) ||\n next < 0 ||\n next > container.length\n ) {\n throw invalid(\n node.path,\n `cannot set list length to ${String(value)}`,\n );\n }\n if (next === container.length) return true;\n const copy = prepareForWrite(node) as unknown as unknown[];\n for (let i = container.length - 1; i >= next; i--) {\n copy.splice(i, 1);\n host.record({ op: \"remove\", path: [...node.path, i] }, node.path);\n }\n gen++;\n return true;\n }\n if (!isIndex(prop)) {\n throw invalid(node.path, `cannot set key \"${prop}\" on a list`);\n }\n const index = Number(prop);\n if (index > container.length) {\n throw invalid(node.path, `list index ${index} out of range`);\n }\n if (index === container.length) {\n if (value === undefined) {\n throw invalid(\n [...node.path, index],\n \"cannot serialize undefined\",\n );\n }\n prepareForWrite(node)[index] = sanitizeValue(value);\n host.record(\n { op: \"add\", path: [...node.path, index], value },\n node.path,\n );\n gen++;\n return true;\n }\n assign(container as unknown as Container, index, value);\n return true;\n }\n assign(container as Container, prop, value);\n return true;\n },\n deleteProperty: (_target, prop) => {\n if (typeof prop === \"symbol\") return false;\n const container = current();\n if (Array.isArray(container)) {\n if (!isIndex(prop) || Number(prop) >= container.length) return true;\n const index = Number(prop);\n (prepareForWrite(node) as unknown as unknown[]).splice(index, 1);\n host.record({ op: \"remove\", path: [...node.path, index] }, node.path);\n gen++;\n return true;\n }\n if (!Object.hasOwn(container as object, prop)) return true;\n if (isUnsafeKey(prop, node.path[node.path.length - 1])) {\n throw invalid(\n [...node.path, prop],\n `cannot remove unsafe key \"${prop}\"`,\n );\n }\n delete prepareForWrite(node)[prop];\n host.record({ op: \"remove\", path: [...node.path, prop] }, [\n ...node.path,\n prop,\n ]);\n gen++;\n return true;\n },\n has: (_target, prop) => Reflect.has(current() as object, prop),\n ...sharedDraftTraps(() => current() as object, \"on a state draft\"),\n });\n return node;\n };\n\n const root = createNode(null, \"\");\n\n return {\n proxy: root.proxy as T,\n flush: () => {\n if (dirty.length === 0) return host.snapshot();\n for (const node of dirty) {\n if (node.copy !== null) {\n node.base = node.copy;\n node.copy = null;\n }\n }\n dirty.length = 0;\n return root.base;\n },\n invalidate: () => {\n gen++;\n },\n };\n};\n\n/**\n * Append `suffix` to the string at `parent[key]` on a statewire state draft,\n * recording a single end-offset text-insert `add` op instead of a full\n * `replace` — use it on token-streaming hot paths. Throws when the current\n * value is not a string.\n */\nexport const append = <TKey extends string | number>(\n parent: { readonly [P in TKey]: string },\n key: TKey,\n suffix: string,\n): void => {\n const handle = (parent as { [NODE]?: NodeHandle })[NODE];\n if (handle === undefined) {\n throw new Error(\"statewire: append expects a state draft object\");\n }\n if (typeof suffix !== \"string\") {\n throw new Error(\"statewire: append suffix must be a string\");\n }\n handle.append(key, suffix);\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"draft.js","names":["Statewire","StatewirePath","sanitizeValue","isUnsafeKey","sharedDraftTraps","DraftHost","snapshot","record","op","Op","changedPath","StateDraft","proxy","T","flush","invalidate","NODE","Symbol","NodeHandle","append","key","suffix","isIndex","test","invalid","path","message","Error","join","Container","Record","codePointLength","text","count","index","length","codePointAt","Node","parent","gen","base","copy","children","Map","isArray","handle","createStateDraft","host","dirty","latest","node","resolve","value","prepareForWrite","Array","slice","push","changedPathFor","applyAppend","rawKey","container","Number","existing","Object","hasOwn","undefined","createNode","seed","childKey","current","childProxy","prop","child","get","set","assign","old","isObjectContainer","Proxy","_target","c","next","isInteger","String","i","splice","deleteProperty","has","Reflect","root","TKey"],"sources":["../../src/host/draft.ts"],"sourcesContent":["import type { Statewire } from \"../types\";\nimport type { StatewirePath } from \"../wire/ops\";\nimport { sanitizeValue } from \"../wire/ops\";\nimport { isUnsafeKey } from \"../util/statePath\";\nimport { sharedDraftTraps } from \"../util/draftTraps\";\n\nexport type DraftHost = {\n snapshot: () => unknown;\n record: (op: Statewire.Op, changedPath: StatewirePath) => void;\n};\n\nexport type StateDraft<T> = {\n proxy: T;\n /** Seal pending writes and return the committed state. */\n flush: () => unknown;\n /** Committed state was replaced externally; cached nodes re-resolve. */\n invalidate: () => void;\n};\n\nconst NODE = Symbol(\"statewire.draftNode\");\n\ntype NodeHandle = {\n append: (key: string | number, suffix: string) => void;\n};\n\nconst isIndex = (key: string) => /^(0|[1-9]\\d*)$/.test(key);\n\nconst invalid = (path: StatewirePath, message: string) =>\n new Error(`statewire: ${message} at path: ${path.join(\".\")}`);\n\ntype Container = Record<string | number, unknown>;\n\n// Text-insert offsets are Unicode code points on the wire, not UTF-16 units.\nconst codePointLength = (text: string): number => {\n let count = 0;\n for (let index = 0; index < text.length; index += 1) {\n if (text.codePointAt(index)! > 0xffff) index += 1;\n count += 1;\n }\n return count;\n};\n\ntype Node = {\n parent: Node | null;\n key: string | number;\n path: StatewirePath;\n gen: number;\n base: unknown;\n copy: Container | null;\n children: Map<string, Node>;\n isArray: boolean;\n proxy: object;\n handle: NodeHandle;\n};\n\nexport const createStateDraft = <T>(host: DraftHost): StateDraft<T> => {\n let gen = 0;\n const dirty: Node[] = [];\n\n const latest = (node: Node): unknown => node.copy ?? node.base;\n\n const resolve = (node: Node): Node => {\n if (node.gen === gen) return node;\n node.gen = gen;\n if (node.parent === null) {\n if (node.copy === null) node.base = host.snapshot();\n return node;\n }\n const value = (latest(resolve(node.parent)) as Container)[node.key];\n if (value !== latest(node)) {\n node.base = value;\n node.copy = null;\n }\n return node;\n };\n\n const prepareForWrite = (node: Node): Container => {\n resolve(node);\n if (node.copy !== null) return node.copy;\n const base = node.base;\n node.copy = Array.isArray(base)\n ? (base.slice() as unknown as Container)\n : { ...(base as Container) };\n dirty.push(node);\n if (node.parent !== null) {\n prepareForWrite(node.parent)[node.key] = node.copy;\n }\n return node.copy;\n };\n\n const changedPathFor = (node: Node, key: string | number): StatewirePath =>\n Array.isArray(latest(node)) ? node.path : [...node.path, key];\n\n const applyAppend = (node: Node, rawKey: string | number, suffix: string) => {\n const container = latest(resolve(node)) as Container;\n const key =\n Array.isArray(container) && typeof rawKey === \"string\" && isIndex(rawKey)\n ? Number(rawKey)\n : rawKey;\n const existing = Object.hasOwn(container as object, key)\n ? container[key]\n : undefined;\n if (typeof existing !== \"string\") {\n throw invalid([...node.path, key], \"cannot append to a non-string\");\n }\n if (suffix === \"\") return;\n prepareForWrite(node)[key] = existing + suffix;\n host.record(\n {\n op: \"add\",\n path: [...node.path, key, codePointLength(existing)],\n value: suffix,\n },\n changedPathFor(node, key),\n );\n };\n\n const createNode = (parent: Node | null, key: string | number): Node => {\n const seed =\n parent === null ? host.snapshot() : (latest(parent) as Container)[key];\n const node: Node = {\n parent,\n key,\n path: parent === null ? [] : [...parent.path, key],\n gen,\n base: seed,\n copy: null,\n children: new Map(),\n isArray: Array.isArray(seed),\n proxy: null!,\n handle: null!,\n };\n node.handle = {\n append: (childKey, suffix) => applyAppend(node, childKey, suffix),\n };\n\n const current = () => latest(resolve(node));\n\n const childProxy = (\n prop: string,\n childKey: string | number,\n value: object,\n ) => {\n let child = node.children.get(prop);\n if (child === undefined || child.isArray !== Array.isArray(value)) {\n child = createNode(node, childKey);\n node.children.set(prop, child);\n }\n return child.proxy;\n };\n\n const assign = (\n container: Container,\n key: string | number,\n value: unknown,\n ) => {\n const old = Object.hasOwn(container as object, key)\n ? container[key]\n : undefined;\n if (old === value) return;\n const isObjectContainer = !Array.isArray(container);\n if (\n isObjectContainer &&\n isUnsafeKey(key, node.path[node.path.length - 1])\n ) {\n throw invalid([...node.path, key], `cannot set unsafe key \"${key}\"`);\n }\n if (value === undefined) {\n if (!isObjectContainer) {\n throw invalid([...node.path, key], \"cannot serialize undefined\");\n }\n delete prepareForWrite(node)[key];\n host.record({ op: \"remove\", path: [...node.path, key] }, [\n ...node.path,\n key,\n ]);\n gen++;\n return;\n }\n prepareForWrite(node)[key] = isObjectContainer\n ? sanitizeValue(value, key as string)\n : sanitizeValue(value);\n host.record(\n { op: \"replace\", path: [...node.path, key], value },\n changedPathFor(node, key),\n );\n if (\n (typeof old === \"object\" && old !== null) ||\n (typeof value === \"object\" && value !== null)\n ) {\n gen++;\n }\n };\n\n node.proxy = new Proxy(seed as object, {\n get: (_target, prop) => {\n if (prop === NODE) return node.handle;\n const container = current();\n if (typeof prop === \"symbol\") {\n return (container as Record<symbol, unknown>)[prop];\n }\n const c = container as Container;\n const key = Array.isArray(c) && isIndex(prop) ? Number(prop) : prop;\n const value = c[key];\n if (typeof value === \"object\" && value !== null) {\n return childProxy(prop, key, value);\n }\n return value;\n },\n set: (_target, prop, value) => {\n if (typeof prop === \"symbol\") return false;\n const container = current();\n if (Array.isArray(container)) {\n if (prop === \"length\") {\n const next = Number(value);\n if (\n !Number.isInteger(next) ||\n next < 0 ||\n next > container.length\n ) {\n throw invalid(\n node.path,\n `cannot set list length to ${String(value)}`,\n );\n }\n if (next === container.length) return true;\n const copy = prepareForWrite(node) as unknown as unknown[];\n for (let i = container.length - 1; i >= next; i--) {\n copy.splice(i, 1);\n host.record({ op: \"remove\", path: [...node.path, i] }, node.path);\n }\n gen++;\n return true;\n }\n if (!isIndex(prop)) {\n throw invalid(node.path, `cannot set key \"${prop}\" on a list`);\n }\n const index = Number(prop);\n if (index > container.length) {\n throw invalid(node.path, `list index ${index} out of range`);\n }\n if (index === container.length) {\n if (value === undefined) {\n throw invalid(\n [...node.path, index],\n \"cannot serialize undefined\",\n );\n }\n prepareForWrite(node)[index] = sanitizeValue(value);\n host.record(\n { op: \"add\", path: [...node.path, index], value },\n node.path,\n );\n gen++;\n return true;\n }\n assign(container as unknown as Container, index, value);\n return true;\n }\n assign(container as Container, prop, value);\n return true;\n },\n deleteProperty: (_target, prop) => {\n if (typeof prop === \"symbol\") return false;\n const container = current();\n if (Array.isArray(container)) {\n if (!isIndex(prop) || Number(prop) >= container.length) return true;\n const index = Number(prop);\n (prepareForWrite(node) as unknown as unknown[]).splice(index, 1);\n host.record({ op: \"remove\", path: [...node.path, index] }, node.path);\n gen++;\n return true;\n }\n if (!Object.hasOwn(container as object, prop)) return true;\n if (isUnsafeKey(prop, node.path[node.path.length - 1])) {\n throw invalid(\n [...node.path, prop],\n `cannot remove unsafe key \"${prop}\"`,\n );\n }\n delete prepareForWrite(node)[prop];\n host.record({ op: \"remove\", path: [...node.path, prop] }, [\n ...node.path,\n prop,\n ]);\n gen++;\n return true;\n },\n has: (_target, prop) => Reflect.has(current() as object, prop),\n ...sharedDraftTraps(() => current() as object, \"on a state draft\"),\n });\n return node;\n };\n\n const root = createNode(null, \"\");\n\n return {\n proxy: root.proxy as T,\n flush: () => {\n if (dirty.length === 0) return host.snapshot();\n for (const node of dirty) {\n if (node.copy !== null) {\n node.base = node.copy;\n node.copy = null;\n }\n }\n dirty.length = 0;\n return root.base;\n },\n invalidate: () => {\n gen++;\n },\n };\n};\n\n/**\n * Append `suffix` to the string at `parent[key]` on a statewire state draft,\n * recording a single end-offset text-insert `add` op instead of a full\n * `replace` — use it on token-streaming hot paths. Throws when the current\n * value is not a string.\n */\nexport const append = <TKey extends string | number>(\n parent: { readonly [P in TKey]: string },\n key: TKey,\n suffix: string,\n): void => {\n const handle = (parent as { [NODE]?: NodeHandle })[NODE];\n if (handle === undefined) {\n throw new Error(\"statewire: append expects a state draft object\");\n }\n if (typeof suffix !== \"string\") {\n throw new Error(\"statewire: append suffix must be a string\");\n }\n handle.append(key, suffix);\n};\n"],"mappings":"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"}
|
package/dist/host/host.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Statewire } from "../types.js";
|
|
2
1
|
import { StatewireHost } from "../host.js";
|
|
2
|
+
import { Statewire } from "../types.js";
|
|
3
3
|
//#region src/host/host.d.ts
|
|
4
4
|
/** Host-side accessor for a `useStatewireState` state object's source (`snapshot()` reads the committed state). */
|
|
5
5
|
declare const getStateHost: (state: object) => StatewireHost.StateHost | undefined;
|
package/dist/host/http.js
CHANGED
|
@@ -98,7 +98,7 @@ const getStream = async (h, request) => {
|
|
|
98
98
|
if ("response" in client) return client.response;
|
|
99
99
|
const attachment = attachSse(h, client.clientId);
|
|
100
100
|
const lease = attachment.registration === null ? null : mintLease();
|
|
101
|
-
if (attachment.registration !== null) attachment.registration.
|
|
101
|
+
if (attachment.registration !== null) attachment.registration.conn.lease = lease;
|
|
102
102
|
return streamResponse(attachment, lease);
|
|
103
103
|
};
|
|
104
104
|
const postStream = async (h, request) => {
|
|
@@ -123,7 +123,7 @@ const postStream = async (h, request) => {
|
|
|
123
123
|
const attachment = attachSse(h, client.clientId);
|
|
124
124
|
if (attachment.registration === null) return streamResponse(attachment, null);
|
|
125
125
|
const lease = mintLease();
|
|
126
|
-
attachment.registration.
|
|
126
|
+
attachment.registration.conn.lease = lease;
|
|
127
127
|
admitAttachFrame(h, attachment.registration, frame);
|
|
128
128
|
return streamResponse(attachment, lease);
|
|
129
129
|
};
|
|
@@ -157,7 +157,8 @@ const frames = async (h, request) => {
|
|
|
157
157
|
if ("response" in client) return client.response;
|
|
158
158
|
const rec = h.clients.get(client.clientId);
|
|
159
159
|
const lease = request.headers.get(LEASE_HEADER);
|
|
160
|
-
|
|
160
|
+
const live = lease !== null && [...h.conns].some((conn) => conn.open && conn.clientId === client.clientId && conn.lease === lease);
|
|
161
|
+
if (h.disposed || h.finished !== null || rec === void 0 || !live) return Response.json({ error: "stale-lease" }, { status: 423 });
|
|
161
162
|
let frame;
|
|
162
163
|
try {
|
|
163
164
|
frame = parseFrame(await request.text(), false);
|
package/dist/host/http.js.map
CHANGED
|
@@ -1 +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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"}
|
|
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","live","some","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.conn.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.conn.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 // A lease lives and dies with its attach: any live attach of the id accepts.\n const live =\n lease !== null &&\n [...h.conns].some(\n (conn) =>\n conn.open && conn.clientId === client.clientId && conn.lease === lease,\n );\n if (h.disposed || h.finished !== null || rec === undefined || !live) {\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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"}
|
package/dist/host/socket.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Statewire } from "../types.js";
|
|
2
|
-
import { StatewirePath } from "../wire/ops.js";
|
|
3
1
|
import { CommandsHost } from "./host.js";
|
|
4
2
|
import { StatewireHost } from "../host.js";
|
|
5
3
|
import { StatewireCmd } from "../client/connection.js";
|
|
4
|
+
import { Statewire } from "../types.js";
|
|
5
|
+
import { StatewirePath } from "../wire/ops.js";
|
|
6
6
|
import { StatewireMetaEntry } from "../wire/meta.js";
|
|
7
7
|
import { StatewireHostInternal } from "../host-internal.js";
|
|
8
8
|
import { ResourceElement } from "@assistant-ui/tap";
|
|
@@ -25,7 +25,6 @@ type ClientRec = {
|
|
|
25
25
|
id: string;
|
|
26
26
|
lastSeq: number;
|
|
27
27
|
ctxSeq: number;
|
|
28
|
-
lease: string | null;
|
|
29
28
|
touched: number;
|
|
30
29
|
context: Record<string, unknown> | null;
|
|
31
30
|
results: Map<number, {
|
|
@@ -40,6 +39,7 @@ type Conn = {
|
|
|
40
39
|
open: boolean;
|
|
41
40
|
sawFrame: boolean;
|
|
42
41
|
lastSentAt: number;
|
|
42
|
+
lease: string | null;
|
|
43
43
|
};
|
|
44
44
|
type CtxStatement = {
|
|
45
45
|
seq: number;
|
|
@@ -77,7 +77,7 @@ type SocketHostState = {
|
|
|
77
77
|
commandsHost: CommandsHost | null;
|
|
78
78
|
current: Statewire<unknown> | null;
|
|
79
79
|
post: () => void;
|
|
80
|
-
connect: (clientId: string, socket: StatewireHostInternal.Socket) => StatewireHostInternal.SocketConnection;
|
|
80
|
+
connect: (clientId: string, socket: StatewireHostInternal.Socket) => StatewireHostInternal.SocketConnection | null;
|
|
81
81
|
finish: (fin: StatewireHost.Fin) => void;
|
|
82
82
|
stream: (request: Request) => Promise<Response>;
|
|
83
83
|
frames: (request: Request) => Promise<Response>;
|
|
@@ -108,7 +108,8 @@ declare const register: (h: SocketHostState, clientId: string, socket: Statewire
|
|
|
108
108
|
* feed it sockets (anything with `send`/`close`) and raw text frames, and it
|
|
109
109
|
* speaks the Statewire packet protocol — snapshot-first hello with `syn`,
|
|
110
110
|
* statement-lane client frames under one admission law, `{cmd}` answer
|
|
111
|
-
* arrays
|
|
111
|
+
* arrays fanned out to every attach of a client id, idle edges, and `fin` on
|
|
112
|
+
* finish.
|
|
112
113
|
*/
|
|
113
114
|
declare const StatewireSocketHost: (transport: ResourceElement<Statewire>, options?: StatewireHostInternal.SocketHostOptions) => StatewireHostInternal.SocketHost;
|
|
114
115
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket.d.ts","names":[],"sources":["../../src/host/socket.ts"],"mappings":";;;;;;;;;cAgBa,cAAY;KAKpB;EACH;EACA,OAAO;EACP;EACA,OAAO;;KAGJ;EACH;EACA;EACA;EACA;EACA;;KAGU;EACV;EACA;EACA;EACA;EACA
|
|
1
|
+
{"version":3,"file":"socket.d.ts","names":[],"sources":["../../src/host/socket.ts"],"mappings":";;;;;;;;;cAgBa,cAAY;KAKpB;EACH;EACA,OAAO;EACP;EACA,OAAO;;KAGJ;EACH;EACA;EACA;EACA;EACA;;KAGU;EACV;EACA;EACA;EACA;EACA,SAAS;EACT,SAAS;IAAc;IAAY,KAAK;;EACxC,UAAU,YAAY;;KAGZ;EACV;EACA,QAAQ,sBAAsB;EAC9B;EACA;EACA;EACA;;KAcG;EAAiB;EAAa,OAAO;;KACrC;EACH;EACA;EACA;EACA;;KAGU;EACV,SAAS;EACT,KAAK;EACL,KAAK;;cAgDM,kBACX,gBACA,mBACC;cAoDU,aAAc,aAAa,mBAAiB;KAmE7C;EACV;EACA;EACA;EACA,WAAW,sBAAsB;EACjC,SAAS,YAAY;EACrB,OAAO,IAAI;EACX,YAAY;EACZ,gBAAgB;EAChB,aAAa,YAAY;EACzB;EACA,UAAU,cAAc;EACxB;EACA;EACA,gBAAgB,kBAAkB;EAClC,WAAW,cAAc;EACzB,cAAc;EACd,SAAS;EACT;EACA,UACE,kBACA,QAAQ,sBAAsB,WAC3B,sBAAsB;EAC3B,SAAS,KAAK,cAAc;EAC5B,SAAS,SAAS,YAAY,QAAQ;EACtC,SAAS,SAAS,YAAY,QAAQ;;cA+C3B,YACX,GAAG,iBACH,MAAM,MACN,eACA;cAgBW,SAAU,GAAG,iBAAiB,MAAM,MAAM;cAsC1C,QAAS,GAAG,iBAAiB,MAAM,cAAc;KAyOlD;EACN;;EACA;EAAiB;IAAO;MAAO;MAAa;;;;cAErC,aACX,GAAG,iBACH,KAAK,WACL,OAAO,gBACN;KAgES;EACV,KAAK;EACL,MAAM;EACN,YAAY,sBAAsB;;cAGvB,WACX,GAAG,iBACH,kBACA,QAAQ,sBAAsB,WAC7B;;;;;;;;;cAoKU,sBACX,WAAW,gBAAgB,YAC3B,UAAU,sBAAsB,sBAC/B,sBAAsB"}
|