statewire 0.8.1 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/client/connection.d.ts +23 -19
- package/dist/client/connection.d.ts.map +1 -1
- package/dist/client/connection.js +143 -103
- package/dist/client/connection.js.map +1 -1
- package/dist/client/core.d.ts +30 -3
- package/dist/client/core.d.ts.map +1 -1
- package/dist/client/core.js +222 -260
- package/dist/client/core.js.map +1 -1
- package/dist/client/session.d.ts +34 -39
- package/dist/client/session.d.ts.map +1 -1
- package/dist/client/session.js +380 -373
- package/dist/client/session.js.map +1 -1
- package/dist/client/sessionStore.d.ts +1 -0
- package/dist/client/sessionStore.d.ts.map +1 -1
- package/dist/client/sessionStore.js.map +1 -1
- package/dist/client/timing.d.ts +1 -2
- package/dist/client/timing.d.ts.map +1 -1
- package/dist/client/timing.js +1 -5
- package/dist/client/timing.js.map +1 -1
- package/dist/host/draft.d.ts +2 -5
- package/dist/host/draft.d.ts.map +1 -1
- package/dist/host/draft.js.map +1 -1
- package/dist/host/host.d.ts +5 -8
- package/dist/host/host.d.ts.map +1 -1
- package/dist/host/host.js +23 -32
- package/dist/host/host.js.map +1 -1
- package/dist/host/socket.d.ts +3 -2
- package/dist/host/socket.d.ts.map +1 -1
- package/dist/host/socket.js +484 -424
- package/dist/host/socket.js.map +1 -1
- package/dist/host-internal.d.ts +4 -0
- package/dist/host-internal.d.ts.map +1 -1
- package/dist/host.d.ts +10 -10
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +2 -2
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/statewire.d.ts +29 -24
- package/dist/statewire.d.ts.map +1 -1
- package/dist/statewire.js +8 -2
- package/dist/statewire.js.map +1 -1
- package/dist/transports/fake-sse.d.ts +1 -1
- package/dist/transports/fake-sse.d.ts.map +1 -1
- package/dist/transports/fake-sse.js +5 -2
- package/dist/transports/fake-sse.js.map +1 -1
- package/dist/transports/fixtures.d.ts +1 -2
- package/dist/transports/fixtures.d.ts.map +1 -1
- package/dist/transports/fixtures.js +5 -7
- package/dist/transports/fixtures.js.map +1 -1
- package/dist/transports/remote.d.ts +2 -1
- package/dist/transports/remote.d.ts.map +1 -1
- package/dist/transports/remote.js +10 -4
- package/dist/transports/remote.js.map +1 -1
- package/dist/transports/sse.d.ts +32 -46
- package/dist/transports/sse.d.ts.map +1 -1
- package/dist/transports/sse.js +56 -59
- package/dist/transports/sse.js.map +1 -1
- package/dist/transports/testing.d.ts +7 -5
- package/dist/transports/testing.d.ts.map +1 -1
- package/dist/transports/testing.js +41 -14
- package/dist/transports/testing.js.map +1 -1
- package/dist/transports/ws.d.ts +2 -3
- package/dist/transports/ws.d.ts.map +1 -1
- package/dist/transports/ws.js +25 -80
- package/dist/transports/ws.js.map +1 -1
- package/dist/types.d.ts +8 -8
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -4
- package/dist/types.js.map +1 -1
- package/dist/wire/ops.d.ts +1 -5
- package/dist/wire/ops.d.ts.map +1 -1
- package/dist/wire/ops.js +6 -5
- package/dist/wire/ops.js.map +1 -1
- package/package.json +1 -1
- package/src/client/connection.ts +175 -128
- package/src/client/core.ts +287 -278
- package/src/client/session.ts +474 -480
- package/src/client/sessionStore.ts +1 -0
- package/src/client/timing.ts +0 -6
- package/src/host/draft.ts +1 -3
- package/src/host/host.ts +41 -58
- package/src/host/socket.ts +653 -448
- package/src/host-internal.ts +4 -0
- package/src/host.ts +13 -10
- package/src/index.ts +0 -1
- package/src/statewire.ts +38 -32
- package/src/transports/fake-sse.ts +2 -1
- package/src/transports/fixtures.ts +4 -9
- package/src/transports/remote.ts +12 -5
- package/src/transports/sse.ts +99 -115
- package/src/transports/testing.ts +47 -19
- package/src/transports/ws.ts +44 -115
- package/src/types.ts +15 -14
- package/src/wire/ops.ts +13 -16
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","names":["useEffect","useState","applyOps","StatewirePath","StatewireResponse","bindCommandsHost","CommandsInvocation","CommandsListener","Statewire","StatewireClient","createMemorySessionStore","StatewireSessionRecord","StatewireSessionStore","StatewireSessionStoreOptions","retryDelay","createMethodProxy","randomId","StatewireSendError","RemoteLink","StatewireRemoteProps","StatewireSubmitResult","StatewireWireCommand","PendingState","at","pendingMarked","failedAttempts","posted","Pending","seq","command","inv","state","resolve","payload","reject","error","Outcome","ok","failure","SendFailure","isResolved","isStateSettled","inFlight","Error","legalTransitions","Record","queued","trying","done","transition","s","Session","entry","next","outcome","prev","includes","settles","undefined","covers","lifecycle","onCovered","onSettle","type","pending","filter","p","carried","Exclude","newClientId","rejectedSend","Promise","result","catch","AttachState","StatewireSessionOptions","record","ctx","recordPersisted","store","lastAck","context","canonical","lastPublished","changed","source","StateSource","attach","attachEpoch","running","pumping","transport","waiters","notify","emitEvent","event","send","commands","params","createSession","options","sessionStore","stored","load","clientId","nextSeq","onSnapshot","args","handleSnapshot","onOps","handleOps","onFinish","handleFinish","snapshot","takeChanged","setOnChange","listener","sendCommand","method","setListener","publish","wake","waiter","waitForWake","r","push","sleepUntilWake","delayMs","clearTimeout","timer","setTimeout","w","settleAll","detach","verdict","cause","evicted","unmount","gone","superseded","const","batch","detachFailure","fate","predecessorLost","some","claimReplays","claimed","length","evict","replays","save","old","enqueue","noteAcked","onAck","spliceAcked","settleVoid","protocolError","message","applyResponse","response","find","code","ingestOps","ops","applied","changedPaths","events","publishIngested","lastSeq","connection","reconnect","pump","res","ack","horizon","Math","max","reason","isFinished","sendable","epoch","onPosted","console","start","stop","setTransport","ts","Date","now","onStart","setContext","value","Array","isArray","useStatewireSession","emit"],"sources":["../../src/client/session.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { applyOps } from \"../wire/ops\";\nimport type { StatewirePath } from \"../wire/ops\";\nimport type { StatewireResponse } from \"./connection\";\nimport { bindCommandsHost } from \"../host/host\";\nimport type { CommandsInvocation, CommandsListener } from \"../host/host\";\nimport type { Statewire } from \"../types\";\nimport type { StatewireClient } from \"../statewire\";\nimport { createMemorySessionStore } from \"./sessionStore\";\nimport type {\n StatewireSessionRecord,\n StatewireSessionStore,\n StatewireSessionStoreOptions,\n} from \"./sessionStore\";\nimport { retryDelay } from \"./timing\";\nimport { createMethodProxy } from \"../util/methodProxy\";\nimport { randomId } from \"../util/randomId\";\nimport { StatewireSendError } from \"../types\";\nimport type {\n RemoteLink,\n StatewireRemoteProps,\n StatewireSubmitResult,\n StatewireWireCommand,\n} from \"../transports/sse\";\n\n/**\n * queued/trying/posted: overlay live, result pending. awaiting-result: acked\n * (overlay gone) but the result rides a late res; `posted: false` re-sends.\n * settled-live: result settled but the overlay stays until the covering ack.\n * done: removed from the queue.\n */\ntype PendingState =\n | { at: \"queued\"; pendingMarked: boolean }\n | { at: \"trying\"; pendingMarked: boolean; failedAttempts: number }\n | { at: \"posted\"; pendingMarked: boolean; failedAttempts: number }\n | { at: \"awaiting-result\"; posted: boolean; failedAttempts: number }\n | { at: \"settled-live\"; posted: boolean; failedAttempts: number }\n | { at: \"done\" };\n\ntype Pending = {\n readonly seq: number;\n readonly command: StatewireWireCommand;\n /** null for context submissions — they carry no command lifecycle. */\n readonly inv: CommandsInvocation | null;\n state: PendingState;\n resolve: (payload: unknown) => void;\n reject: (error: StatewireSendError) => void;\n};\n\ntype Outcome =\n | { ok: true; payload: unknown }\n | { ok: false; failure: StatewireClient.SendFailure };\n\nconst isResolved = (state: PendingState) =>\n state.at === \"settled-live\" || state.at === \"done\";\n\nconst isStateSettled = (state: PendingState) =>\n state.at === \"awaiting-result\" || state.at === \"done\";\n\n// The pump admits an entry as trying before sending; only the pump itself\n// requeues, and a done entry is never still pending.\nconst inFlight = (state: PendingState) => {\n if (state.at === \"queued\" || state.at === \"done\") {\n throw new Error(`statewire: ${state.at} entry observed after a send`);\n }\n return state;\n};\n\nconst legalTransitions: Record<PendingState[\"at\"], PendingState[\"at\"][]> = {\n queued: [\"queued\", \"trying\", \"awaiting-result\", \"settled-live\", \"done\"],\n // trying -> queued: an answered unknown-client proves the seq was never admitted.\n trying: [\n \"queued\",\n \"trying\",\n \"posted\",\n \"awaiting-result\",\n \"settled-live\",\n \"done\",\n ],\n posted: [\"posted\", \"trying\", \"awaiting-result\", \"settled-live\", \"done\"],\n \"awaiting-result\": [\"awaiting-result\", \"done\"],\n \"settled-live\": [\"settled-live\", \"done\"],\n done: [],\n};\n\nconst transition = (\n s: Session,\n entry: Pending,\n next: PendingState,\n outcome?: Outcome,\n) => {\n const prev = entry.state;\n if (!legalTransitions[prev.at].includes(next.at)) {\n throw new Error(\n `statewire: illegal pending transition ${prev.at} -> ${next.at}`,\n );\n }\n const settles = !isResolved(prev) && isResolved(next);\n if (settles && outcome === undefined) {\n throw new Error(\n `statewire: settling transition ${prev.at} -> ${next.at} without an outcome`,\n );\n }\n const covers = !isStateSettled(prev) && isStateSettled(next);\n entry.state = next;\n if (covers && entry.inv) s.lifecycle?.onCovered?.(entry.inv);\n if (settles && outcome !== undefined) {\n if (outcome.ok) {\n entry.resolve(outcome.payload);\n if (entry.inv)\n s.lifecycle?.onSettle(entry.inv, {\n type: \"accepted\",\n payload: outcome.payload,\n });\n } else {\n entry.reject(new StatewireSendError(outcome.failure));\n if (entry.inv)\n s.lifecycle?.onSettle(entry.inv, {\n type: \"rejected\",\n error: new StatewireSendError(outcome.failure),\n });\n }\n }\n if (next.at === \"done\") {\n s.pending = s.pending.filter((p) => p !== entry);\n }\n};\n\nconst carried = (\n state: Exclude<PendingState, { at: \"done\" }>,\n): { posted: boolean; failedAttempts: number } =>\n state.at === \"queued\"\n ? { posted: false, failedAttempts: 0 }\n : state.at === \"trying\" || state.at === \"posted\"\n ? { posted: state.at === \"posted\", failedAttempts: state.failedAttempts }\n : { posted: state.posted, failedAttempts: state.failedAttempts };\n\nconst newClientId = () => `c-${randomId()}`;\n\nconst rejectedSend = (\n failure: StatewireClient.SendFailure,\n): Promise<unknown> => {\n const result = Promise.reject<unknown>(new StatewireSendError(failure));\n result.catch(() => {});\n return result;\n};\n\ntype AttachState = \"detached\" | \"attached\" | \"finished\";\n\nexport type StatewireSessionOptions = StatewireSessionStoreOptions;\n\ntype Session = {\n record: StatewireSessionRecord;\n ctx: StatewireRemoteProps;\n recordPersisted: boolean;\n store: StatewireSessionStore;\n lastAck: number;\n context: Record<string, unknown> | undefined;\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 emitEvent: (event: unknown) => void;\n send: (command: StatewireWireCommand) => Promise<unknown>;\n commands: Record<string, (...params: unknown[]) => Promise<unknown>>;\n};\n\nconst createSession = (options: StatewireSessionOptions): Session => {\n const store = options.sessionStore ?? createMemorySessionStore();\n const stored = store.load();\n const record = stored ?? { clientId: newClientId(), nextSeq: 1 };\n const s: Session = {\n record,\n ctx: {\n get clientId() {\n return s.record.clientId;\n },\n onSnapshot: (args) => handleSnapshot(s, args),\n onOps: (args) => handleOps(s, args),\n onFinish: (args) => handleFinish(s, args),\n },\n recordPersisted: stored !== null,\n store,\n lastAck: -1,\n context: undefined,\n pending: [],\n canonical: undefined,\n lastPublished: undefined,\n changed: [],\n source: {\n snapshot: () => s.canonical,\n takeChanged: () => {\n const changed = s.changed;\n s.changed = [];\n return changed;\n },\n setOnChange: (listener) => {\n s.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 emitEvent: () => {},\n send: (command) => sendCommand(s, command),\n commands: createMethodProxy((method, params) => s.send({ method, params })),\n };\n bindCommandsHost(s.commands, {\n setListener: (listener) => {\n s.lifecycle = listener;\n },\n });\n return s;\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 settleAll = (\n s: Session,\n detach: \"evicted\" | \"unmount\" | \"gone\" | \"superseded\",\n verdict?: {\n entry: Pending;\n failure: StatewireClient.SendFailure;\n },\n) => {\n const cause = (\n {\n evicted: \"session-lost\",\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 const state = entry.state;\n if (state.at === \"awaiting-result\")\n return { fate: \"result-unavailable\", cause };\n if (state.at === \"queued\") {\n const predecessorLost = batch.some(\n (p) => p.seq < entry.seq && p.state.at !== \"queued\",\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 const failure =\n entry === verdict?.entry ? verdict.failure : detachFailure(entry);\n transition(s, entry, { at: \"done\" }, { ok: false, failure });\n }\n};\n\n// Claims every unsettled real command for replay under the rotated client id,\n// in seq order; context submissions settle and the context re-sends whole.\nconst claimReplays = (s: Session): Pending[] => {\n const claimed = s.pending.filter(\n (entry) => entry.inv !== null && !isResolved(entry.state),\n );\n if (claimed.length === 0) return [];\n s.pending = s.pending.filter((p) => !claimed.includes(p));\n return claimed;\n};\n\nconst evict = (\n s: Session,\n verdict?: {\n entry: Pending;\n failure: StatewireClient.SendFailure;\n },\n) => {\n const replays = claimReplays(s);\n settleAll(s, \"evicted\", verdict);\n s.record = { clientId: newClientId(), nextSeq: 1 };\n s.store.save(s.record);\n s.attach = \"detached\";\n // The server dropped this client's record and its stored context with it:\n // the new session opens with a fresh full root replace.\n if (s.context !== undefined) void s.send({ context: s.context });\n for (const old of replays) {\n if (old.inv) s.lifecycle?.onCovered?.(old.inv);\n enqueue(s, old.command, old.resolve, old.reject);\n }\n publish(s);\n wake(s);\n};\n\nconst noteAcked = (s: Session, seq: number) => {\n for (const entry of s.pending) {\n if (entry.seq > seq || isStateSettled(entry.state)) continue;\n if (entry.inv) s.lifecycle?.onAck(entry.inv);\n }\n};\n\nconst spliceAcked = (s: Session, seq: number, settleVoid: boolean) => {\n for (const entry of [...s.pending]) {\n const state = entry.state;\n if (entry.seq > seq || isStateSettled(state)) continue;\n if (state.at === \"settled-live\") {\n transition(s, entry, { at: \"done\" });\n } else if (settleVoid && !state.pendingMarked) {\n transition(s, entry, { at: \"done\" }, { ok: true, payload: undefined });\n } else {\n transition(s, entry, { at: \"awaiting-result\", ...carried(state) });\n }\n }\n};\n\nconst protocolError = (message: string): never => {\n throw new Error(`statewire: protocol error: ${message}`);\n};\n\nconst applyResponse = (s: Session, response: StatewireResponse) => {\n const entry = s.pending.find((p) => p.seq === response.seq);\n if (!entry) return;\n const state = entry.state;\n if (state.at === \"done\") return;\n switch (response.type) {\n case \"pending\":\n if (\n state.at === \"queued\" ||\n state.at === \"trying\" ||\n state.at === \"posted\"\n ) {\n transition(s, entry, { ...state, pendingMarked: true });\n }\n break;\n case \"accepted\":\n if (state.at === \"settled-live\") break;\n transition(\n s,\n entry,\n state.at === \"awaiting-result\"\n ? { at: \"done\" }\n : { at: \"settled-live\", ...carried(state) },\n { ok: true, payload: response.payload },\n );\n break;\n case \"rejected\":\n transition(\n s,\n entry,\n { at: \"done\" },\n {\n ok: false,\n failure:\n response.code === \"unknown-command\"\n ? { fate: \"unknown-command\", cause: \"server\" }\n : response.code === \"invalid-params\"\n ? {\n fate: \"invalid-params\",\n cause: \"server\",\n message:\n response.message ??\n protocolError(\"rejected response missing message\"),\n }\n : {\n fate: \"rejected\",\n cause: \"server\",\n message:\n response.message ??\n protocolError(\"rejected response missing message\"),\n ...(response.payload !== undefined && {\n payload: response.payload,\n }),\n },\n },\n );\n break;\n case \"crashed\":\n case \"result-unavailable\":\n if (state.at === \"settled-live\") break;\n transition(\n s,\n entry,\n state.at === \"awaiting-result\"\n ? { at: \"done\" }\n : { at: \"settled-live\", ...carried(state) },\n { ok: false, failure: { fate: response.type, cause: \"server\" } },\n );\n break;\n }\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 return applied.events;\n};\n\nconst publishIngested = (s: Session, events: unknown[]) => {\n publish(s);\n for (const event of events) s.emitEvent(event);\n wake(s);\n};\n\nconst handleSnapshot = (\n s: Session,\n { ops, lastSeq }: { ops: unknown[]; lastSeq: number },\n) => {\n if (lastSeq === -1 && s.attach !== \"detached\") {\n evict(s);\n s.transport?.connection.reconnect();\n publishIngested(s, ingestOps(s, ops));\n return;\n }\n const events = ingestOps(s, ops);\n s.attach = \"attached\";\n s.attachEpoch += 1;\n // The attach snapshot is not the true covering envelope: acked entries\n // stay awaiting their result and re-send under seq dedup.\n noteAcked(s, lastSeq);\n spliceAcked(s, lastSeq, false);\n s.lastAck = lastSeq;\n for (const entry of s.pending) {\n const state = entry.state;\n if (entry.seq <= lastSeq) {\n if (state.at === \"awaiting-result\" && state.posted) {\n transition(s, entry, { ...state, posted: false });\n }\n if (state.at === \"awaiting-result\") void pump(s);\n } else if (state.at === \"posted\") {\n // Above the ack watermark a prior receipt no longer counts.\n transition(s, entry, {\n at: \"trying\",\n pendingMarked: state.pendingMarked,\n failedAttempts: state.failedAttempts,\n });\n void pump(s);\n } else if (state.at === \"settled-live\" && state.posted) {\n transition(s, entry, { ...state, posted: false });\n void pump(s);\n }\n }\n publishIngested(s, events);\n};\n\nconst handleOps = (\n s: Session,\n {\n ops,\n res,\n ack,\n }: { ops: unknown[]; res?: StatewireResponse[]; ack?: number },\n) => {\n const horizon = ack === undefined ? s.lastAck : Math.max(s.lastAck, ack);\n if (res !== undefined) {\n for (const response of res) {\n if (response.seq > horizon) {\n protocolError(\"response seq beyond the ack\");\n }\n }\n }\n const events = ingestOps(s, ops);\n if (ack !== undefined) noteAcked(s, ack);\n if (res !== undefined) for (const response of res) applyResponse(s, response);\n if (ack !== undefined) {\n spliceAcked(s, ack, true);\n s.lastAck = Math.max(s.lastAck, ack);\n }\n publishIngested(s, events);\n};\n\nconst handleFinish = (s: Session, { reason }: { reason: string }) => {\n if (reason === \"evicted\" || reason === \"error\") return;\n s.attach = \"finished\";\n settleAll(s, reason === \"superseded\" ? \"superseded\" : \"gone\");\n publish(s);\n wake(s);\n};\n\nconst isFinished = (s: Session) => s.attach === \"finished\";\n\nconst sendable = (p: Pending) =>\n p.state.at === \"queued\" ||\n p.state.at === \"trying\" ||\n ((p.state.at === \"awaiting-result\" || p.state.at === \"settled-live\") &&\n !p.state.posted);\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 entry = s.pending.find(sendable);\n if (!entry) return;\n const transport = s.transport;\n if (s.attach !== \"attached\" || !transport) {\n await waitForWake(s);\n continue;\n }\n const clientId = s.record.clientId;\n const epoch = s.attachEpoch;\n if (entry.state.at === \"queued\") {\n transition(s, entry, {\n at: \"trying\",\n pendingMarked: entry.state.pendingMarked,\n failedAttempts: 0,\n });\n }\n let result: StatewireSubmitResult;\n try {\n result = await transport.send({\n seq: entry.seq,\n command: entry.command,\n });\n } catch (error) {\n if (!s.running || isFinished(s)) return;\n if (!s.pending.includes(entry)) continue;\n const state = inFlight(entry.state);\n transition(s, entry, {\n ...state,\n failedAttempts: state.failedAttempts + 1,\n });\n await sleepUntilWake(s, retryDelay(error, state.failedAttempts + 1));\n continue;\n }\n if (!s.running) return;\n if (!s.pending.includes(entry)) continue;\n if (result.type === \"receipt\") {\n const response = result.response;\n const state = entry.state;\n if (state.at === \"trying\" || state.at === \"posted\") {\n transition(s, entry, {\n at: \"posted\",\n pendingMarked: state.pendingMarked,\n failedAttempts: state.failedAttempts,\n });\n } else if (\n state.at === \"awaiting-result\" ||\n state.at === \"settled-live\"\n ) {\n transition(s, entry, { ...state, posted: true });\n }\n if (entry.inv) s.lifecycle?.onPosted?.(entry.inv);\n if (response !== undefined) {\n applyResponse(s, response);\n publish(s);\n }\n } else if (result.type === \"rejected\") {\n transition(\n s,\n entry,\n { at: \"done\" },\n {\n ok: false,\n failure: {\n fate: \"rejected\",\n cause: \"server\",\n message: result.message,\n },\n },\n );\n publish(s);\n } else if (result.type === \"stale-lease\") {\n if (epoch === s.attachEpoch) await waitForWake(s);\n } else if (result.type === \"unknown-client\") {\n // Eviction is never decided on the POST leg: re-attach first and let\n // the snapshot's lastSeq deliver the verdict. An answered attempt\n // proves the server never admitted this seq, so a clean entry requeues.\n if (epoch === s.attachEpoch) {\n const state = entry.state;\n if (state.at === \"trying\" && state.failedAttempts === 0) {\n transition(s, entry, {\n at: \"queued\",\n pendingMarked: state.pendingMarked,\n });\n }\n s.transport?.connection.reconnect();\n while (\n s.running &&\n !isFinished(s) &&\n epoch === s.attachEpoch &&\n clientId === s.record.clientId\n ) {\n await waitForWake(s);\n }\n }\n } else if (result.type === \"seq-gap\") {\n if (clientId === s.record.clientId) {\n console.error(\n `statewire: server reports a seq gap for seq ${entry.seq}; ` +\n \"treating the session as corrupt and rotating the client id\",\n );\n // Every attempt answered means the server never saw it; a thrown\n // attempt may have left the machine unanswered, so the verdict is \"lost\".\n const state = inFlight(entry.state);\n const failure: StatewireClient.SendFailure =\n state.at === \"awaiting-result\"\n ? { fate: \"result-unavailable\", cause: \"session-lost\" }\n : {\n fate: state.failedAttempts > 0 ? \"lost\" : \"aborted\",\n cause: \"session-lost\",\n };\n evict(s, { entry, failure });\n s.transport?.connection.reconnect();\n }\n }\n }\n } finally {\n s.pumping = false;\n }\n};\n\nconst start = (s: Session) => {\n if (!s.recordPersisted) {\n s.recordPersisted = true;\n s.store.save(s.record);\n }\n s.running = true;\n void pump(s);\n};\n\nconst stop = (s: Session) => {\n s.running = false;\n wake(s);\n settleAll(s, \"unmount\");\n};\n\nconst setTransport = (s: Session, transport: RemoteLink) => {\n if (s.transport === transport) return;\n s.transport = transport;\n wake(s);\n};\n\nconst enqueue = (\n s: Session,\n command: StatewireWireCommand,\n resolve: (payload: unknown) => void,\n reject: (error: StatewireSendError) => void,\n) => {\n const seq = s.record.nextSeq;\n s.record = { ...s.record, nextSeq: seq + 1 };\n s.store.save(s.record);\n const entry: Pending = {\n seq,\n command,\n inv:\n command.method === undefined\n ? null\n : {\n method: command.method,\n params: command.params,\n ts: Date.now(),\n seq,\n clientId: s.record.clientId,\n },\n state: { at: \"queued\", pendingMarked: false },\n resolve,\n reject,\n };\n s.pending.push(entry);\n if (entry.inv) s.lifecycle?.onStart(entry.inv);\n void pump(s);\n};\n\nconst sendCommand = (\n s: Session,\n command: StatewireWireCommand,\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 enqueue(s, command, resolve, reject);\n });\n result.catch(() => {});\n return result;\n};\n\nconst setContext = (\n s: Session,\n value: Record<string, unknown>,\n): Promise<unknown> => {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new Error(\"statewire: context must be a plain object\");\n }\n s.context = value;\n return s.send({ context: value });\n};\n\nexport const useStatewireSession = (\n options: StatewireSessionOptions,\n emit: (event: unknown) => void,\n): Session => {\n const [s] = useState(() => createSession(options));\n useEffect(() => {\n s.emitEvent = emit;\n return () => {\n s.emitEvent = () => {};\n };\n }, [s, emit]);\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":["useEffect","useState","applyOps","StatewirePath","StatewireCmd","StatewireSyn","encodeParams","encodeValue","reviveResponse","bindCommandsHost","CommandsInvocation","CommandsListener","Statewire","StatewireClient","createMemorySessionStore","StatewireSessionRecord","StatewireSessionStore","StatewireSessionStoreOptions","backoff","createMethodProxy","randomId","StatewireSendError","RemoteLink","StatewireFrame","StatewireRemoteProps","WireCommand","method","params","meta","Pending","seq","command","inv","attempted","posted","applied","resolve","payload","reject","error","CtxPending","ops","protocolError","message","Error","newClientId","rejectedSend","failure","SendFailure","Promise","result","catch","AttachState","StatewireSessionOptions","onDone","Session","record","ctx","recordPersisted","store","ephemeral","sessionKnown","durableSeq","resumeAt","idle","ctxOps","ctxPending","lanesOnAttach","attachLaneSeqs","ReadonlySet","frameFailures","pending","canonical","lastPublished","changed","source","StateSource","attach","attachEpoch","running","pumping","transport","waiters","lifecycle","notify","commands","Record","publish","s","wake","waiter","waitForWake","r","push","sleepUntilWake","delayMs","clearTimeout","timer","setTimeout","filter","w","setRecord","save","setIdle","level","cover","entry","onApplied","onCovered","settleEntry","outcome","ok","p","onSettle","type","settleAll","detach","cause","unmount","gone","superseded","const","batch","detachFailure","predecessorLost","some","fate","restateContext","undefined","ctxNextSeq","resetRecord","clientId","survivors","nextSeq","old","enqueue","ephemeralDetach","rejectedFailure","cmd","code","handleCmd","dur","Math","max","find","String","handleCmds","cmds","ingestOps","state","changedPaths","publishIngested","RECORDS","snapClocks","seqWm","ctxWm","tail","cmdAligned","every","i","length","ctxAligned","c","handleHello","syn","includes","attachCmd","attachCtx","Set","has","ctxWatermark","ctxSeq","pump","handleOps","handleRefusal","watermark","retry","Date","now","firstCmd","firstCtx","clocksAhead","connection","reconnect","handleDetach","handleFinish","reason","isFinished","Lanes","frame","cmdEntries","ctxEntries","buildLanes","at","map","takeAttachLanes","wanted","lanes","status","resumeDelay","epoch","send","onPosted","start","stop","setTransport","ts","onStart","sendCommand","encoded","setContext","value","Array","isArray","op","path","useStatewireSession","options","defaultStore","sessionStore","stored","load","session","onHello","args","onOps","onCmds","onRefusal","onIdle","onDetach","onFinish","snapshot","takeChanged","setOnChange","listener","setListener"],"sources":["../../src/client/session.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { applyOps } from \"../wire/ops\";\nimport type { StatewirePath } from \"../wire/ops\";\nimport type { StatewireCmd, StatewireSyn } from \"./connection\";\nimport { encodeParams, encodeValue, reviveResponse } from \"../wire/meta\";\nimport { bindCommandsHost } from \"../host/host\";\nimport type { CommandsInvocation, CommandsListener } from \"../host/host\";\nimport type { Statewire } from \"../types\";\nimport type { StatewireClient } from \"../statewire\";\nimport { createMemorySessionStore } from \"./sessionStore\";\nimport type {\n StatewireSessionRecord,\n StatewireSessionStore,\n StatewireSessionStoreOptions,\n} from \"./sessionStore\";\nimport { backoff } from \"./timing\";\nimport { createMethodProxy } from \"../util/methodProxy\";\nimport { randomId } from \"../util/randomId\";\nimport { StatewireSendError } from \"../types\";\nimport type {\n RemoteLink,\n StatewireFrame,\n StatewireRemoteProps,\n} from \"../transports/sse\";\n\ntype WireCommand = {\n method: string;\n params: unknown[];\n meta?: unknown;\n};\n\ntype Pending = {\n readonly seq: number;\n readonly command: WireCommand;\n readonly inv: CommandsInvocation;\n /** A send carrying this statement may have reached the server. */\n attempted: boolean;\n /** In the resend queue when false. */\n posted: boolean;\n /** The server acknowledged admission; the terminal verdict is still due. */\n applied: boolean;\n resolve: (payload: unknown) => void;\n reject: (error: StatewireSendError) => void;\n};\n\ntype CtxPending = {\n readonly seq: number;\n readonly ops: unknown[];\n posted: boolean;\n};\n\nconst protocolError = (message: string): never => {\n throw new Error(`statewire: protocol error: ${message}`);\n};\n\nconst newClientId = () => `c-${randomId()}`;\n\nconst rejectedSend = (\n failure: StatewireClient.SendFailure,\n): Promise<unknown> => {\n const result = Promise.reject<unknown>(new StatewireSendError(failure));\n result.catch(() => {});\n return result;\n};\n\ntype AttachState = \"detached\" | \"attached\" | \"finished\";\n\nexport type StatewireSessionOptions = StatewireSessionStoreOptions & {\n onDone?: (() => void) | undefined;\n};\n\ntype Session = {\n record: StatewireSessionRecord;\n ctx: StatewireRemoteProps;\n recordPersisted: boolean;\n store: StatewireSessionStore;\n ephemeral: boolean;\n sessionKnown: boolean;\n durableSeq: number;\n resumeAt: number;\n idle: boolean;\n onDone: (() => void) | undefined;\n ctxOps: unknown[] | undefined;\n ctxPending: CtxPending[];\n lanesOnAttach: boolean;\n attachLaneSeqs: readonly [ReadonlySet<number>, ReadonlySet<number>];\n frameFailures: number;\n pending: Pending[];\n canonical: unknown;\n lastPublished: unknown;\n changed: StatewirePath[];\n source: Statewire.StateSource;\n attach: AttachState;\n attachEpoch: number;\n running: boolean;\n pumping: boolean;\n transport: RemoteLink | null;\n waiters: (() => void)[];\n lifecycle: CommandsListener | null;\n notify: () => void;\n commands: Record<string, (...params: unknown[]) => Promise<unknown>>;\n};\n\nconst publish = (s: Session) => {\n if (s.canonical === s.lastPublished) return;\n s.lastPublished = s.canonical;\n s.notify();\n};\n\nconst wake = (s: Session) => {\n const waiters = s.waiters;\n s.waiters = [];\n for (const waiter of waiters) waiter();\n};\n\nconst waitForWake = (s: Session) => new Promise<void>((r) => s.waiters.push(r));\n\nconst sleepUntilWake = (s: Session, delayMs: number) =>\n new Promise<void>((resolve) => {\n const waiter = () => {\n clearTimeout(timer);\n resolve();\n };\n const timer = setTimeout(() => {\n s.waiters = s.waiters.filter((w) => w !== waiter);\n resolve();\n }, delayMs);\n s.waiters.push(waiter);\n });\n\nconst setRecord = (s: Session, record: StatewireSessionRecord) => {\n s.record = record;\n if (!s.ephemeral) s.store.save(record);\n};\n\nconst setIdle = (s: Session, level: boolean) => {\n if (level === s.idle) return;\n s.idle = level;\n s.notify();\n if (level) s.onDone?.();\n};\n\nconst cover = (s: Session, entry: Pending) => {\n if (entry.applied) return;\n entry.applied = true;\n s.lifecycle?.onApplied(entry.inv);\n s.lifecycle?.onCovered?.(entry.inv);\n};\n\nconst settleEntry = (\n s: Session,\n entry: Pending,\n outcome:\n | { ok: true; payload: unknown }\n | { ok: false; failure: StatewireClient.SendFailure },\n) => {\n s.pending = s.pending.filter((p) => p !== entry);\n if (outcome.ok) {\n entry.resolve(outcome.payload);\n s.lifecycle?.onSettle(entry.inv, {\n type: \"accepted\",\n payload: outcome.payload,\n });\n } else {\n entry.reject(new StatewireSendError(outcome.failure));\n s.lifecycle?.onSettle(entry.inv, {\n type: \"rejected\",\n error: new StatewireSendError(outcome.failure),\n });\n }\n};\n\nconst settleAll = (s: Session, detach: \"unmount\" | \"gone\" | \"superseded\") => {\n const cause = (\n {\n unmount: \"unmount\",\n gone: \"stream-gone\",\n superseded: \"superseded\",\n } as const\n )[detach];\n const batch = s.pending;\n const detachFailure = (entry: Pending): StatewireClient.SendFailure => {\n if (!entry.attempted) {\n const predecessorLost = batch.some(\n (p) => p.seq < entry.seq && p.attempted,\n );\n return predecessorLost\n ? { fate: \"aborted\", cause, predecessorLost }\n : { fate: \"aborted\", cause };\n }\n return { fate: \"lost\", cause };\n };\n for (const entry of [...batch]) {\n s.lifecycle?.onCovered?.(entry.inv);\n settleEntry(s, entry, { ok: false, failure: detachFailure(entry) });\n }\n};\n\nconst restateContext = (s: Session) => {\n s.ctxPending = [];\n if (s.ctxOps === undefined) return;\n const seq = s.record.ctxNextSeq;\n setRecord(s, { ...s.record, ctxNextSeq: seq + 1 });\n s.ctxPending.push({ seq, ops: s.ctxOps, posted: false });\n};\n\n// The server-side record's dedup memory is gone: history that may have\n// reached a server settles lost — the new hello's snapshot is the only\n// truth about what survived — and never-attempted statements re-stamp\n// from 1 on the reset lane clocks.\nconst resetRecord = (s: Session, clientId: string) => {\n const survivors: Pending[] = [];\n for (const entry of [...s.pending]) {\n if (entry.attempted || entry.applied) {\n s.lifecycle?.onCovered?.(entry.inv);\n settleEntry(s, entry, {\n ok: false,\n failure: { fate: \"lost\", cause: \"session-lost\" },\n });\n } else {\n survivors.push(entry);\n }\n }\n s.pending = [];\n s.durableSeq = 0;\n setRecord(s, { clientId, nextSeq: 1, ctxNextSeq: 1 });\n for (const old of survivors) {\n s.lifecycle?.onCovered?.(old.inv);\n enqueue(s, old.command, old.inv.params, old.resolve, old.reject);\n }\n restateContext(s);\n publish(s);\n wake(s);\n};\n\n// The record died with the attach: mint a fresh id for the next one.\nconst ephemeralDetach = (s: Session) => {\n s.ephemeral = false;\n s.sessionKnown = false;\n resetRecord(s, newClientId());\n};\n\nconst rejectedFailure = (cmd: StatewireCmd): StatewireClient.SendFailure => {\n const code =\n cmd.code === undefined ||\n cmd.code === \"unknown-command\" ||\n cmd.code === \"invalid-params\"\n ? cmd.code\n : protocolError(`unknown rejected code: ${cmd.code}`);\n return {\n fate: \"rejected\",\n cause: \"server\",\n ...(code !== undefined && { code }),\n ...(cmd.message !== undefined && { message: cmd.message }),\n ...(cmd.payload !== undefined && { payload: cmd.payload }),\n };\n};\n\nconst handleCmd = (s: Session, cmd: StatewireCmd) => {\n if (typeof cmd.seq !== \"number\") protocolError(\"cmd entry missing seq\");\n if (cmd.dur === true) {\n s.durableSeq = Math.max(s.durableSeq, cmd.seq);\n for (const entry of s.pending) {\n if (entry.seq <= s.durableSeq) entry.posted = true;\n }\n }\n if (cmd.type === undefined) return;\n const entry = s.pending.find((p) => p.seq === cmd.seq);\n if (cmd.type === \"applied\") {\n if (entry) cover(s, entry);\n return;\n }\n if (!entry) return;\n cover(s, entry);\n const outcome =\n cmd.type === \"result\"\n ? { ok: true as const, payload: reviveResponse(cmd).payload }\n : cmd.type === \"rejected\"\n ? { ok: false as const, failure: rejectedFailure(reviveResponse(cmd)) }\n : cmd.type === \"failed\"\n ? {\n ok: false as const,\n failure: {\n fate: \"failed\" as const,\n cause: \"server\" as const,\n ...(cmd.message !== undefined && { message: cmd.message }),\n },\n }\n : cmd.type === \"crashed\"\n ? {\n ok: false as const,\n failure: { fate: \"crashed\" as const, cause: \"server\" as const },\n }\n : cmd.type === \"expired\"\n ? {\n ok: false as const,\n failure: {\n fate: \"expired\" as const,\n cause: \"server\" as const,\n },\n }\n : protocolError(`unknown cmd type: ${String(cmd.type)}`);\n settleEntry(s, entry, outcome);\n};\n\nconst handleCmds = (s: Session, cmds: StatewireCmd[]) => {\n for (const cmd of cmds) handleCmd(s, cmd);\n wake(s);\n};\n\nconst ingestOps = (s: Session, ops: unknown[]) => {\n const applied = applyOps(s.canonical, ops);\n s.canonical = applied.state;\n s.changed.push(...applied.changedPaths);\n};\n\nconst publishIngested = (s: Session) => {\n publish(s);\n wake(s);\n};\n\nconst RECORDS = [\"fresh\", \"resumed\", \"ephemeral\"];\n\n// Snap a resumed session's lane clocks onto the record: seqs above the stated\n// watermark were never admitted, so a stale restored clock re-stamps its\n// never-admitted statements consecutively after the watermark.\nconst snapClocks = (s: Session, seqWm: number, ctxWm: number) => {\n const tail = s.pending.filter((p) => p.seq > seqWm);\n const cmdAligned =\n tail.every((p, i) => p.seq === seqWm + 1 + i) &&\n s.record.nextSeq === seqWm + tail.length + 1;\n if (!cmdAligned) {\n s.pending = s.pending.filter((p) => p.seq <= seqWm);\n setRecord(s, { ...s.record, nextSeq: seqWm + 1 });\n for (const old of tail) {\n s.lifecycle?.onCovered?.(old.inv);\n enqueue(s, old.command, old.inv.params, old.resolve, old.reject);\n }\n }\n const ctxAligned =\n s.ctxPending.every((c, i) => c.seq === ctxWm + 1 + i) &&\n s.record.ctxNextSeq === ctxWm + s.ctxPending.length + 1;\n if (!ctxAligned) {\n setRecord(s, { ...s.record, ctxNextSeq: ctxWm + 1 });\n restateContext(s);\n }\n};\n\nconst handleHello = (\n s: Session,\n { ops, syn }: { ops: unknown[]; syn: StatewireSyn },\n) => {\n const record = syn.record ?? \"fresh\";\n if (!RECORDS.includes(record)) {\n protocolError(`unknown record: ${String(record)}`);\n }\n ingestOps(s, ops);\n s.attach = \"attached\";\n s.attachEpoch += 1;\n s.frameFailures = 0;\n s.resumeAt = 0;\n s.ephemeral = record === \"ephemeral\";\n const [attachCmd, attachCtx] = s.attachLaneSeqs;\n s.attachLaneSeqs = [new Set(), new Set()];\n if (record === \"resumed\") {\n // Resending unsettled statements is exactly-once-safe: absorbed replays\n // re-emit their stored verdicts (applied while still in flight).\n for (const entry of s.pending) {\n entry.posted = attachCmd.has(entry.seq);\n }\n const ctxWatermark = syn.ctxSeq ?? 0;\n s.ctxPending = s.ctxPending.filter((c) => c.seq > ctxWatermark);\n for (const c of s.ctxPending) c.posted = attachCtx.has(c.seq);\n snapClocks(s, syn.seq ?? 0, ctxWatermark);\n } else {\n resetRecord(s, s.record.clientId);\n }\n s.sessionKnown = true;\n publishIngested(s);\n void pump(s);\n};\n\nconst handleOps = (\n s: Session,\n { ops, cmds }: { ops: unknown[]; cmds: StatewireCmd[] },\n) => {\n ingestOps(s, ops);\n for (const cmd of cmds) handleCmd(s, cmd);\n publishIngested(s);\n};\n\n// A {syn} refusal never settles: reconcile the resend queues against the\n// stated watermarks; retry present means wait, absent means resend now.\nconst handleRefusal = (s: Session, syn: StatewireSyn) => {\n const { seq, ctxSeq } = syn;\n if (typeof seq === \"number\") {\n const watermark = Math.max(seq, s.durableSeq);\n for (const entry of s.pending) entry.posted = entry.seq <= watermark;\n }\n if (typeof ctxSeq === \"number\") {\n s.ctxPending = s.ctxPending.filter((c) => c.seq > ctxSeq);\n for (const c of s.ctxPending) c.posted = false;\n }\n if (typeof syn.retry === \"number\") {\n s.resumeAt = Date.now() + Math.max(0, syn.retry);\n }\n const firstCmd = s.pending.find((p) => !p.posted);\n const firstCtx = s.ctxPending.find((c) => !c.posted);\n const clocksAhead =\n (typeof syn.seq === \"number\" &&\n firstCmd !== undefined &&\n firstCmd.seq > Math.max(syn.seq, s.durableSeq) + 1) ||\n (typeof syn.ctxSeq === \"number\" &&\n firstCtx !== undefined &&\n firstCtx.seq > syn.ctxSeq + 1);\n // Clocks ahead of a record that cannot admit them: reattach first and let\n // the hello's record arbitrate.\n if (clocksAhead) s.transport?.connection.reconnect();\n wake(s);\n void pump(s);\n};\n\nconst handleDetach = (s: Session) => {\n if (s.attach === \"attached\") s.attach = \"detached\";\n if (s.ephemeral) ephemeralDetach(s);\n wake(s);\n};\n\nconst handleFinish = (s: Session, { reason }: { reason: string }) => {\n if (reason === \"evicted\" || reason === \"error\") return;\n s.attach = \"finished\";\n settleAll(s, reason === \"superseded\" ? \"superseded\" : \"gone\");\n publish(s);\n wake(s);\n};\n\nconst isFinished = (s: Session) => s.attach === \"finished\";\n\ntype Lanes = {\n frame: StatewireFrame;\n cmdEntries: Pending[];\n ctxEntries: CtxPending[];\n};\n\n// A lane must be strictly consecutive within a frame: the first unposted run\n// per lane rides now, later runs ride follow-up frames.\nconst buildLanes = (s: Session): Lanes | null => {\n const ctxEntries: CtxPending[] = [];\n for (const entry of s.ctxPending) {\n if (entry.posted) continue;\n if (ctxEntries.length > 0 && entry.seq !== ctxEntries.at(-1)!.seq + 1)\n break;\n ctxEntries.push(entry);\n }\n const cmdEntries: Pending[] = [];\n for (const entry of s.pending) {\n if (entry.posted) continue;\n if (cmdEntries.length > 0 && entry.seq !== cmdEntries.at(-1)!.seq + 1)\n break;\n cmdEntries.push(entry);\n }\n if (ctxEntries.length === 0 && cmdEntries.length === 0) return null;\n return {\n frame: {\n ...(ctxEntries.length > 0 && {\n ctx: ctxEntries.map((c) => ({ ops: c.ops, seq: c.seq })),\n }),\n ...(cmdEntries.length > 0 && {\n cmd: cmdEntries.map((p) => ({ ...p.command, seq: p.seq })),\n }),\n },\n cmdEntries,\n ctxEntries,\n };\n};\n\nconst takeAttachLanes = (s: Session): StatewireFrame | null => {\n s.attachLaneSeqs = [new Set(), new Set()];\n const wanted = s.lanesOnAttach;\n s.lanesOnAttach = false;\n if (!wanted || !s.sessionKnown) return null;\n const lanes = buildLanes(s);\n if (!lanes) return null;\n s.attachLaneSeqs = [\n new Set(lanes.cmdEntries.map((p) => p.seq)),\n new Set(lanes.ctxEntries.map((c) => c.seq)),\n ];\n for (const entry of lanes.cmdEntries) entry.attempted = true;\n return lanes.frame;\n};\n\nconst pump = async (s: Session) => {\n if (s.pumping) return;\n s.pumping = true;\n try {\n while (s.running && !isFinished(s)) {\n const transport = s.transport;\n if (buildLanes(s) === null) return;\n if (s.attach !== \"attached\" || !transport) {\n if (transport?.connection.status === \"idle\") {\n transport.connection.reconnect();\n }\n await waitForWake(s);\n continue;\n }\n const resumeDelay = s.resumeAt - Date.now();\n if (resumeDelay > 0) {\n await sleepUntilWake(s, resumeDelay);\n continue;\n }\n const lanes = buildLanes(s);\n if (!lanes) continue;\n const epoch = s.attachEpoch;\n for (const entry of lanes.cmdEntries) entry.attempted = true;\n let result;\n try {\n result = await transport.send(lanes.frame);\n } catch {\n if (!s.running || isFinished(s)) return;\n s.frameFailures += 1;\n await sleepUntilWake(s, backoff(s.frameFailures));\n continue;\n }\n if (!s.running) return;\n s.frameFailures = 0;\n if (result.type === \"ok\") {\n if (epoch === s.attachEpoch) {\n for (const entry of lanes.cmdEntries) {\n if (s.pending.includes(entry)) {\n entry.posted = true;\n s.lifecycle?.onPosted?.(entry.inv);\n }\n }\n for (const entry of lanes.ctxEntries) entry.posted = true;\n }\n } else if (result.type === \"refusal\") {\n if (epoch === s.attachEpoch) handleRefusal(s, result.syn);\n } else if (result.type === \"stale-lease\") {\n // A POST outlived its attach: reattach first, the hello arbitrates.\n if (epoch === s.attachEpoch) {\n transport.connection.reconnect();\n await waitForWake(s);\n }\n } else if (result.type === \"no-lease\") {\n // No out-of-band write channel: the frame rides a fresh attach.\n if (epoch === s.attachEpoch) {\n s.lanesOnAttach = true;\n transport.connection.reconnect();\n while (s.running && !isFinished(s) && epoch === s.attachEpoch) {\n await waitForWake(s);\n }\n }\n }\n }\n } finally {\n s.pumping = false;\n }\n};\n\nconst start = (s: Session) => {\n if (!s.recordPersisted) {\n s.recordPersisted = true;\n s.store.save(s.record);\n }\n s.running = true;\n void pump(s);\n};\n\nconst stop = (s: Session) => {\n s.running = false;\n wake(s);\n settleAll(s, \"unmount\");\n};\n\nconst setTransport = (s: Session, transport: RemoteLink) => {\n if (s.transport === transport) return;\n s.transport = transport;\n wake(s);\n};\n\nconst enqueue = (\n s: Session,\n command: WireCommand,\n params: readonly unknown[],\n resolve: (payload: unknown) => void,\n reject: (error: StatewireSendError) => void,\n) => {\n const seq = s.record.nextSeq;\n setRecord(s, { ...s.record, nextSeq: seq + 1 });\n const entry: Pending = {\n seq,\n command,\n inv: {\n method: command.method,\n params,\n ts: Date.now(),\n seq,\n clientId: s.record.clientId,\n },\n attempted: false,\n posted: false,\n applied: false,\n resolve,\n reject,\n };\n s.pending.push(entry);\n s.lifecycle?.onStart(entry.inv);\n void pump(s);\n};\n\nconst sendCommand = (\n s: Session,\n method: string,\n params: readonly unknown[],\n): Promise<unknown> => {\n if (!s.running) {\n return rejectedSend({ fate: \"aborted\", cause: \"unmount\" });\n }\n if (isFinished(s)) {\n return rejectedSend({ fate: \"aborted\", cause: \"stream-gone\" });\n }\n const result = new Promise<unknown>((resolve, reject) => {\n const { params: encoded, meta } = encodeParams(params);\n enqueue(\n s,\n { method, params: encoded, ...(meta !== undefined && { meta }) },\n params,\n resolve,\n reject,\n );\n });\n result.catch(() => {});\n return result;\n};\n\nconst setContext = (s: Session, value: Record<string, unknown>): void => {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new Error(\"statewire: context must be a plain object\");\n }\n const { value: encoded, meta } = encodeValue(value);\n s.ctxOps = [\n {\n op: \"replace\",\n path: [],\n value: encoded,\n ...(meta !== undefined && { meta }),\n },\n ];\n const seq = s.record.ctxNextSeq;\n setRecord(s, { ...s.record, ctxNextSeq: seq + 1 });\n s.ctxPending.push({ seq, ops: s.ctxOps, posted: false });\n wake(s);\n void pump(s);\n};\n\nexport const useStatewireSession = (\n options: StatewireSessionOptions,\n): Session => {\n const [defaultStore] = useState(createMemorySessionStore);\n const store = options.sessionStore ?? defaultStore;\n const [s] = useState((): Session => {\n const stored = store.load();\n const record = stored ?? {\n clientId: newClientId(),\n nextSeq: 1,\n ctxNextSeq: 1,\n };\n const session: Session = {\n record,\n ctx: {\n get clientId() {\n return session.record.clientId;\n },\n takeAttachLanes: () => takeAttachLanes(session),\n onHello: (args) => handleHello(session, args),\n onOps: (args) => handleOps(session, args),\n onCmds: (cmds) => handleCmds(session, cmds),\n onRefusal: (syn) => handleRefusal(session, syn),\n onIdle: (level) => setIdle(session, level),\n onDetach: () => handleDetach(session),\n onFinish: (args) => handleFinish(session, args),\n },\n recordPersisted: stored !== null,\n store,\n ephemeral: false,\n sessionKnown: false,\n durableSeq: 0,\n resumeAt: 0,\n idle: false,\n onDone: undefined,\n ctxOps: undefined,\n ctxPending: [],\n lanesOnAttach: false,\n attachLaneSeqs: [new Set(), new Set()],\n frameFailures: 0,\n pending: [],\n canonical: undefined,\n lastPublished: undefined,\n changed: [],\n source: {\n snapshot: () => session.canonical,\n takeChanged: () => {\n const changed = session.changed;\n session.changed = [];\n return changed;\n },\n setOnChange: (listener) => {\n session.notify = listener ?? (() => {});\n },\n },\n attach: \"detached\",\n attachEpoch: 0,\n running: false,\n pumping: false,\n transport: null,\n waiters: [],\n lifecycle: null,\n notify: () => {},\n commands: createMethodProxy((method, params) =>\n sendCommand(session, method, params),\n ),\n };\n bindCommandsHost(session.commands, {\n setListener: (listener) => {\n session.lifecycle = listener;\n },\n });\n return session;\n });\n s.store = store;\n s.onDone = options.onDone;\n useEffect(() => {\n start(s);\n return () => stop(s);\n }, [s]);\n return s;\n};\n\nexport { setContext, setTransport };\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionStore.d.ts","names":[],"sources":["../../src/client/sessionStore.ts"],"mappings":";KAAY;EACV;EACA;;KAGU;EACV,YAAY;EACZ,OAAO,QAAQ;;KAGL;EACV,eAAe;;cAGJ,gCAA+B"}
|
|
1
|
+
{"version":3,"file":"sessionStore.d.ts","names":[],"sources":["../../src/client/sessionStore.ts"],"mappings":";KAAY;EACV;EACA;EACA;;KAGU;EACV,YAAY;EACZ,OAAO,QAAQ;;KAGL;EACV,eAAe;;cAGJ,gCAA+B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionStore.js","names":["StatewireSessionRecord","clientId","nextSeq","StatewireSessionStore","load","save","record","StatewireSessionStoreOptions","sessionStore","createMemorySessionStore","next"],"sources":["../../src/client/sessionStore.ts"],"sourcesContent":["export type StatewireSessionRecord = {\n clientId: string;\n nextSeq: number;\n};\n\nexport type StatewireSessionStore = {\n load: () => StatewireSessionRecord | null;\n save: (record: StatewireSessionRecord) => void;\n};\n\nexport type StatewireSessionStoreOptions = {\n sessionStore?: StatewireSessionStore | undefined;\n};\n\nexport const createMemorySessionStore = (): StatewireSessionStore => {\n let record: StatewireSessionRecord | null = null;\n return {\n load: () => record,\n save: (next) => {\n record = next;\n },\n };\n};\n"],"mappings":";
|
|
1
|
+
{"version":3,"file":"sessionStore.js","names":["StatewireSessionRecord","clientId","nextSeq","ctxNextSeq","StatewireSessionStore","load","save","record","StatewireSessionStoreOptions","sessionStore","createMemorySessionStore","next"],"sources":["../../src/client/sessionStore.ts"],"sourcesContent":["export type StatewireSessionRecord = {\n clientId: string;\n nextSeq: number;\n ctxNextSeq: number;\n};\n\nexport type StatewireSessionStore = {\n load: () => StatewireSessionRecord | null;\n save: (record: StatewireSessionRecord) => void;\n};\n\nexport type StatewireSessionStoreOptions = {\n sessionStore?: StatewireSessionStore | undefined;\n};\n\nexport const createMemorySessionStore = (): StatewireSessionStore => {\n let record: StatewireSessionRecord | null = null;\n return {\n load: () => record,\n save: (next) => {\n record = next;\n },\n };\n};\n"],"mappings":";AAeA,MAAaU,iCAAwD;CACnE,IAAIH,SAAwC;CAC5C,OAAO;EACLF,YAAYE;EACZD,OAAOK,SAAS;GACdJ,SAASI;EACX;CACF;AACF"}
|
package/dist/client/timing.d.ts
CHANGED
|
@@ -5,7 +5,6 @@ declare const timing: {
|
|
|
5
5
|
heartbeatTimeoutMs: number;
|
|
6
6
|
};
|
|
7
7
|
declare const backoff: (attempt: number) => number;
|
|
8
|
-
declare const retryDelay: (error: unknown, attempt: number) => number;
|
|
9
8
|
//#endregion
|
|
10
|
-
export { backoff,
|
|
9
|
+
export { backoff, timing };
|
|
11
10
|
//# sourceMappingURL=timing.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timing.d.ts","names":[],"sources":["../../src/client/timing.ts"],"mappings":";cAAa;;;;;cAMA,UAAW
|
|
1
|
+
{"version":3,"file":"timing.d.ts","names":[],"sources":["../../src/client/timing.ts"],"mappings":";cAAa;;;;;cAMA,UAAW"}
|
package/dist/client/timing.js
CHANGED
|
@@ -5,11 +5,7 @@ const timing = {
|
|
|
5
5
|
heartbeatTimeoutMs: 3e4
|
|
6
6
|
};
|
|
7
7
|
const backoff = (attempt) => Math.min(timing.backoffBaseMs * 2 ** (attempt - 1), timing.backoffMaxMs);
|
|
8
|
-
const retryDelay = (error, attempt) => {
|
|
9
|
-
const retryAfterMs = error?.retryAfterMs;
|
|
10
|
-
return typeof retryAfterMs === "number" ? retryAfterMs : backoff(attempt);
|
|
11
|
-
};
|
|
12
8
|
//#endregion
|
|
13
|
-
export { backoff,
|
|
9
|
+
export { backoff, timing };
|
|
14
10
|
|
|
15
11
|
//# sourceMappingURL=timing.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timing.js","names":["timing","backoffBaseMs","backoffMaxMs","heartbeatTimeoutMs","backoff","attempt","Math","min"
|
|
1
|
+
{"version":3,"file":"timing.js","names":["timing","backoffBaseMs","backoffMaxMs","heartbeatTimeoutMs","backoff","attempt","Math","min"],"sources":["../../src/client/timing.ts"],"sourcesContent":["export const timing = {\n backoffBaseMs: 250,\n backoffMaxMs: 30_000,\n heartbeatTimeoutMs: 30_000,\n};\n\nexport const backoff = (attempt: number) =>\n Math.min(timing.backoffBaseMs * 2 ** (attempt - 1), timing.backoffMaxMs);\n"],"mappings":";AAAA,MAAaA,SAAS;CACpBC,eAAe;CACfC,cAAc;CACdC,oBAAoB;AACtB;AAEA,MAAaC,WAAWC,YACtBC,KAAKC,IAAIP,OAAOC,gBAAgB,MAAMI,UAAU,IAAIL,OAAOE,YAAY"}
|
package/dist/host/draft.d.ts
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import { Statewire } from "../types.js";
|
|
2
2
|
import { StatewirePath } from "../wire/ops.js";
|
|
3
3
|
//#region src/host/draft.d.ts
|
|
4
|
-
type ServerOp = Exclude<Statewire.Op, {
|
|
5
|
-
op: "event";
|
|
6
|
-
}>;
|
|
7
4
|
type DraftHost = {
|
|
8
5
|
snapshot: () => unknown;
|
|
9
|
-
record: (op:
|
|
6
|
+
record: (op: Statewire.Op, changedPath: StatewirePath) => void;
|
|
10
7
|
};
|
|
11
8
|
type StateDraft<T> = {
|
|
12
9
|
proxy: T;
|
|
@@ -24,5 +21,5 @@ declare const createStateDraft: <T>(host: DraftHost) => StateDraft<T>;
|
|
|
24
21
|
*/
|
|
25
22
|
declare const append: <TKey extends string | number>(parent: { readonly [P in TKey]: string; }, key: TKey, suffix: string) => void;
|
|
26
23
|
//#endregion
|
|
27
|
-
export { DraftHost,
|
|
24
|
+
export { DraftHost, StateDraft, append, createStateDraft };
|
|
28
25
|
//# sourceMappingURL=draft.d.ts.map
|
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
|
|
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;;cA6BW,mBAAoB,GAAG,MAAM,cAAY,WAAW;;;;;;;cAsQpD,SAAU,8BACrB,oBAAoB,KAAK,kBACzB,KAAK,MACL"}
|
package/dist/host/draft.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"draft.js","names":["Statewire","StatewirePath","sanitizeValue","isUnsafeKey","sharedDraftTraps","ServerOp","Exclude","Op","op","DraftHost","snapshot","record","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 ServerOp = Exclude<Statewire.Op, { op: \"event\" }>;\n\nexport type DraftHost = {\n snapshot: () => unknown;\n record: (op: ServerOp, 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","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":";;;;AAmBA,MAAMgB,OAAOC,OAAO,qBAAqB;AAMzC,MAAMK,WAAWF,QAAgB,iBAAiBG,KAAKH,GAAG;AAE1D,MAAMI,WAAWC,MAAqBC,4BACpC,IAAIC,MAAM,cAAcD,QAAO,YAAaD,KAAKG,KAAK,GAAG,GAAG;AAiB9D,MAAaY,oBAAuBC,SAAmC;CACrE,IAAIR,MAAM;CACV,MAAMS,QAAgB,CAAA;CAEtB,MAAMC,UAAUC,SAAwBA,KAAKT,QAAQS,KAAKV;CAE1D,MAAMW,WAAWD,SAAqB;EACpC,IAAIA,KAAKX,QAAQA,KAAK,OAAOW;EAC7BA,KAAKX,MAAMA;EACX,IAAIW,KAAKZ,WAAW,MAAM;GACxB,IAAIY,KAAKT,SAAS,MAAMS,KAAKV,OAAOO,KAAKnC,SAAS;GAClD,OAAOsC;EACT;EACA,MAAME,QAASH,OAAOE,QAAQD,KAAKZ,MAAM,CAAC,CAAC,CAAeY,KAAKxB;EAC/D,IAAI0B,UAAUH,OAAOC,IAAI,GAAG;GAC1BA,KAAKV,OAAOY;GACZF,KAAKT,OAAO;EACd;EACA,OAAOS;CACT;CAEA,MAAMG,mBAAmBH,SAA0B;EACjDC,QAAQD,IAAI;EACZ,IAAIA,KAAKT,SAAS,MAAM,OAAOS,KAAKT;EACpC,MAAMD,OAAOU,KAAKV;EAClBU,KAAKT,OAAOa,MAAMV,QAAQJ,IAAI,IACzBA,KAAKe,MAAM,IACZ,EAAE,GAAIf,KAAmB;EAC7BQ,MAAMQ,KAAKN,IAAI;EACf,IAAIA,KAAKZ,WAAW,MAClBe,gBAAgBH,KAAKZ,MAAM,CAAC,CAACY,KAAKxB,OAAOwB,KAAKT;EAEhD,OAAOS,KAAKT;CACd;CAEA,MAAMgB,kBAAkBP,MAAYxB,QAClC4B,MAAMV,QAAQK,OAAOC,IAAI,CAAC,IAAIA,KAAKnB,OAAO,CAAC,GAAGmB,KAAKnB,MAAML,GAAG;CAE9D,MAAMgC,eAAeR,MAAYS,QAAyBhC,WAAmB;EAC3E,MAAMiC,YAAYX,OAAOE,QAAQD,IAAI,CAAC;EACtC,MAAMxB,MACJ4B,MAAMV,QAAQgB,SAAS,KAAK,OAAOD,WAAW,YAAY/B,QAAQ+B,MAAM,IACpEE,OAAOF,MAAM,IACbA;EACN,MAAMG,WAAWC,OAAOC,OAAOJ,WAAqBlC,GAAG,IACnDkC,UAAUlC,OACVuC,KAAAA;EACJ,IAAI,OAAOH,aAAa,UACtB,MAAMhC,QAAQ,CAAC,GAAGoB,KAAKnB,MAAML,GAAG,GAAG,+BAA+B;EAEpE,IAAIC,WAAW,IAAI;EACnB0B,gBAAgBH,IAAI,CAAC,CAACxB,OAAOoC,WAAWnC;EACxCoB,KAAKlC,OACH;GAAEC,IAAI;GAAOiB,MAAM;IAAC,GAAGmB,KAAKnB;IAAML;IAAKoC,SAASI;GAAM;GAAGd,OAAOzB;EAAO,GACvE8B,eAAeP,MAAMxB,GAAG,CAC1B;CACF;CAEA,MAAMyC,cAAc7B,QAAqBZ,QAA+B;EACtE,MAAM0C,OACJ9B,WAAW,OAAOS,KAAKnC,SAAS,IAAKqC,OAAOX,MAAM,CAAC,CAAeZ;EACpE,MAAMwB,OAAa;GACjBZ;GACAZ;GACAK,MAAMO,WAAW,OAAO,CAAA,IAAK,CAAC,GAAGA,OAAOP,MAAML,GAAG;GACjDa;GACAC,MAAM4B;GACN3B,MAAM;GACNC,0BAAU,IAAIC,IAAI;GAClBC,SAASU,MAAMV,QAAQwB,IAAI;GAC3BlD,OAAO;GACP2B,QAAQ;EACV;EACAK,KAAKL,SAAS,EACZpB,SAAS4C,UAAU1C,WAAW+B,YAAYR,MAAMmB,UAAU1C,MAAM,EAClE;EAEA,MAAM2C,gBAAgBrB,OAAOE,QAAQD,IAAI,CAAC;EAE1C,MAAMqB,cACJC,MACAH,UACAjB,UACG;GACH,IAAIqB,QAAQvB,KAAKR,SAASgC,IAAIF,IAAI;GAClC,IAAIC,UAAUR,KAAAA,KAAaQ,MAAM7B,YAAYU,MAAMV,QAAQQ,KAAK,GAAG;IACjEqB,QAAQN,WAAWjB,MAAMmB,QAAQ;IACjCnB,KAAKR,SAASiC,IAAIH,MAAMC,KAAK;GAC/B;GACA,OAAOA,MAAMvD;EACf;EAEA,MAAM0D,UACJhB,WACAlC,KACA0B,UACG;GACH,MAAMyB,MAAMd,OAAOC,OAAOJ,WAAqBlC,GAAG,IAC9CkC,UAAUlC,OACVuC,KAAAA;GACJ,IAAIY,QAAQzB,OAAO;GACnB,MAAM0B,oBAAoB,CAACxB,MAAMV,QAAQgB,SAAS;GAClD,IACEkB,qBACArE,YAAYiB,KAAKwB,KAAKnB,KAAKmB,KAAKnB,KAAKmC,SAAS,EAAE,GAEhD,MAAMpC,QAAQ,CAAC,GAAGoB,KAAKnB,MAAML,GAAG,GAAG,0BAA0BA,IAAG,EAAG;GAErE,IAAI0B,UAAUa,KAAAA,GAAW;IACvB,IAAI,CAACa,mBACH,MAAMhD,QAAQ,CAAC,GAAGoB,KAAKnB,MAAML,GAAG,GAAG,4BAA4B;IAEjE,OAAO2B,gBAAgBH,IAAI,CAAC,CAACxB;IAC7BqB,KAAKlC,OAAO;KAAEC,IAAI;KAAUiB,MAAM,CAAC,GAAGmB,KAAKnB,MAAML,GAAG;IAAE,GAAG,CACvD,GAAGwB,KAAKnB,MACRL,GAAG,CACJ;IACDa;IACA;GACF;GACAc,gBAAgBH,IAAI,CAAC,CAACxB,OAAOoD,oBACzBtE,cAAc4C,OAAO1B,GAAa,IAClClB,cAAc4C,KAAK;GACvBL,KAAKlC,OACH;IAAEC,IAAI;IAAWiB,MAAM,CAAC,GAAGmB,KAAKnB,MAAML,GAAG;IAAG0B;GAAM,GAClDK,eAAeP,MAAMxB,GAAG,CAC1B;GACA,IACG,OAAOmD,QAAQ,YAAYA,QAAQ,QACnC,OAAOzB,UAAU,YAAYA,UAAU,MAExCb;EAEJ;EAEAW,KAAKhC,QAAQ,IAAI6D,MAAMX,MAAgB;GACrCM,MAAMM,SAASR,SAAS;IACtB,IAAIA,SAASlD,MAAM,OAAO4B,KAAKL;IAC/B,MAAMe,YAAYU,QAAQ;IAC1B,IAAI,OAAOE,SAAS,UAClB,OAAQZ,UAAsCY;IAEhD,MAAMS,IAAIrB;IACV,MAAMlC,MAAM4B,MAAMV,QAAQqC,CAAC,KAAKrD,QAAQ4C,IAAI,IAAIX,OAAOW,IAAI,IAAIA;IAC/D,MAAMpB,QAAQ6B,EAAEvD;IAChB,IAAI,OAAO0B,UAAU,YAAYA,UAAU,MACzC,OAAOmB,WAAWC,MAAM9C,KAAK0B,KAAK;IAEpC,OAAOA;GACT;GACAuB,MAAMK,SAASR,MAAMpB,UAAU;IAC7B,IAAI,OAAOoB,SAAS,UAAU,OAAO;IACrC,MAAMZ,YAAYU,QAAQ;IAC1B,IAAIhB,MAAMV,QAAQgB,SAAS,GAAG;KAC5B,IAAIY,SAAS,UAAU;MACrB,MAAMU,OAAOrB,OAAOT,KAAK;MACzB,IACE,CAACS,OAAOsB,UAAUD,IAAI,KACtBA,OAAO,KACPA,OAAOtB,UAAUM,QAEjB,MAAMpC,QACJoB,KAAKnB,MACL,6BAA6BqD,OAAOhC,KAAK,GAC3C;MAEF,IAAI8B,SAAStB,UAAUM,QAAQ,OAAO;MACtC,MAAMzB,OAAOY,gBAAgBH,IAAI;MACjC,KAAK,IAAImC,IAAIzB,UAAUM,SAAS,GAAGmB,KAAKH,MAAMG,KAAK;OACjD5C,KAAK6C,OAAOD,GAAG,CAAC;OAChBtC,KAAKlC,OAAO;QAAEC,IAAI;QAAUiB,MAAM,CAAC,GAAGmB,KAAKnB,MAAMsD,CAAC;OAAE,GAAGnC,KAAKnB,IAAI;MAClE;MACAQ;MACA,OAAO;KACT;KACA,IAAI,CAACX,QAAQ4C,IAAI,GACf,MAAM1C,QAAQoB,KAAKnB,MAAM,mBAAmByC,KAAI,YAAa;KAE/D,MAAMe,QAAQ1B,OAAOW,IAAI;KACzB,IAAIe,QAAQ3B,UAAUM,QACpB,MAAMpC,QAAQoB,KAAKnB,MAAM,cAAcwD,MAAK,cAAe;KAE7D,IAAIA,UAAU3B,UAAUM,QAAQ;MAC9B,IAAId,UAAUa,KAAAA,GACZ,MAAMnC,QACJ,CAAC,GAAGoB,KAAKnB,MAAMwD,KAAK,GACpB,4BACF;MAEFlC,gBAAgBH,IAAI,CAAC,CAACqC,SAAS/E,cAAc4C,KAAK;MAClDL,KAAKlC,OACH;OAAEC,IAAI;OAAOiB,MAAM,CAAC,GAAGmB,KAAKnB,MAAMwD,KAAK;OAAGnC;MAAM,GAChDF,KAAKnB,IACP;MACAQ;MACA,OAAO;KACT;KACAqC,OAAOhB,WAAmC2B,OAAOnC,KAAK;KACtD,OAAO;IACT;IACAwB,OAAOhB,WAAwBY,MAAMpB,KAAK;IAC1C,OAAO;GACT;GACAoC,iBAAiBR,SAASR,SAAS;IACjC,IAAI,OAAOA,SAAS,UAAU,OAAO;IACrC,MAAMZ,YAAYU,QAAQ;IAC1B,IAAIhB,MAAMV,QAAQgB,SAAS,GAAG;KAC5B,IAAI,CAAChC,QAAQ4C,IAAI,KAAKX,OAAOW,IAAI,KAAKZ,UAAUM,QAAQ,OAAO;KAC/D,MAAMqB,QAAQ1B,OAAOW,IAAI;KACzB,gBAAiBtB,IAAI,CAAC,CAA0BoC,OAAOC,OAAO,CAAC;KAC/DxC,KAAKlC,OAAO;MAAEC,IAAI;MAAUiB,MAAM,CAAC,GAAGmB,KAAKnB,MAAMwD,KAAK;KAAE,GAAGrC,KAAKnB,IAAI;KACpEQ;KACA,OAAO;IACT;IACA,IAAI,CAACwB,OAAOC,OAAOJ,WAAqBY,IAAI,GAAG,OAAO;IACtD,IAAI/D,YAAY+D,MAAMtB,KAAKnB,KAAKmB,KAAKnB,KAAKmC,SAAS,EAAE,GACnD,MAAMpC,QACJ,CAAC,GAAGoB,KAAKnB,MAAMyC,IAAI,GACnB,6BAA6BA,KAAI,EACnC;IAEF,OAAOnB,gBAAgBH,IAAI,CAAC,CAACsB;IAC7BzB,KAAKlC,OAAO;KAAEC,IAAI;KAAUiB,MAAM,CAAC,GAAGmB,KAAKnB,MAAMyC,IAAI;IAAE,GAAG,CACxD,GAAGtB,KAAKnB,MACRyC,IAAI,CACL;IACDjC;IACA,OAAO;GACT;GACAkD,MAAMT,SAASR,SAASkB,QAAQD,IAAInB,QAAQ,GAAaE,IAAI;GAC7D,GAAG9D,uBAAuB4D,QAAQ,GAAa,kBAAkB;EACnE,CAAC;EACD,OAAOpB;CACT;CAEA,MAAMyC,OAAOxB,WAAW,MAAM,EAAE;CAEhC,OAAO;EACLjD,OAAOyE,KAAKzE;EACZE,aAAa;GACX,IAAI4B,MAAMkB,WAAW,GAAG,OAAOnB,KAAKnC,SAAS;GAC7C,KAAK,MAAMsC,QAAQF,OACjB,IAAIE,KAAKT,SAAS,MAAM;IACtBS,KAAKV,OAAOU,KAAKT;IACjBS,KAAKT,OAAO;GACd;GAEFO,MAAMkB,SAAS;GACf,OAAOyB,KAAKnD;EACd;EACAnB,kBAAkB;GAChBkB;EACF;CACF;AACF;;;;;;;AAQA,MAAad,UACXa,QACAZ,KACAC,WACS;CACT,MAAMkB,SAAUP,OAAmChB;CACnD,IAAIuB,WAAWoB,KAAAA,GACb,MAAM,IAAIhC,MAAM,gDAAgD;CAElE,IAAI,OAAON,WAAW,UACpB,MAAM,IAAIM,MAAM,2CAA2C;CAE7DY,OAAOpB,OAAOC,KAAKC,MAAM;AAC3B"}
|
package/dist/host/host.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ type CommandsOutcome = {
|
|
|
21
21
|
type CommandsListener = {
|
|
22
22
|
onStart: (inv: CommandsInvocation) => void;
|
|
23
23
|
onPosted?: (inv: CommandsInvocation) => void;
|
|
24
|
-
|
|
24
|
+
onApplied: (inv: CommandsInvocation) => void;
|
|
25
25
|
/** Canonical state now covers the invocation's effect — drop any overlay. */
|
|
26
26
|
onCovered?: (inv: CommandsInvocation) => void;
|
|
27
27
|
onSettle: (inv: CommandsInvocation, outcome: CommandsOutcome) => void;
|
|
@@ -31,21 +31,18 @@ type CommandsSubmission = {
|
|
|
31
31
|
params: readonly unknown[];
|
|
32
32
|
clientId?: string;
|
|
33
33
|
caller?: StatewireHost.Caller;
|
|
34
|
-
|
|
34
|
+
onApplied?: () => void;
|
|
35
35
|
onSettle?: (outcome: CommandsOutcome) => void;
|
|
36
36
|
};
|
|
37
37
|
type CommandsHost = {
|
|
38
38
|
setListener: (listener: CommandsListener | null) => void;
|
|
39
39
|
submit?: (call: CommandsSubmission) => Promise<unknown>;
|
|
40
|
+
/** Registered command names, read live. */
|
|
41
|
+
methods?: () => string[];
|
|
40
42
|
};
|
|
41
43
|
declare const getCommandsHost: (commands: object) => CommandsHost | undefined;
|
|
42
44
|
declare const bindCommandsHost: (commands: object, host: CommandsHost) => void;
|
|
43
45
|
declare const useStatewireCommands: <THandlers extends Statewire.Commands>(commands: (THandlers & ThisType<StatewireHost.Ctx>) | undefined | null | false) => Statewire.CommandsProxy<StatewireHost.HandledCommands<THandlers>>;
|
|
44
|
-
type EmitHost = {
|
|
45
|
-
setSink: (sink: ((event: unknown) => void) | null) => void;
|
|
46
|
-
};
|
|
47
|
-
declare const getEmitHost: (emit: object | undefined) => EmitHost | undefined;
|
|
48
|
-
declare const useStatewireEmit: <TEvent>() => ((event: TEvent) => void);
|
|
49
46
|
//#endregion
|
|
50
|
-
export { CommandsHost, CommandsInvocation, CommandsListener, CommandsOutcome, CommandsSubmission,
|
|
47
|
+
export { CommandsHost, CommandsInvocation, CommandsListener, CommandsOutcome, CommandsSubmission, bindCommandsHost, getCommandsHost, getStateHost, useStatewireCommands, useStatewireState };
|
|
51
48
|
//# sourceMappingURL=host.d.ts.map
|
package/dist/host/host.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.d.ts","names":[],"sources":["../../src/host/host.ts"],"mappings":";;;;cAmBa,eACX,kBACC,cAAc;cAaJ,oBAAqB,QAChC,YAAY,qBACD,QAAQ,cAAc,SAAS;
|
|
1
|
+
{"version":3,"file":"host.d.ts","names":[],"sources":["../../src/host/host.ts"],"mappings":";;;;cAmBa,eACX,kBACC,cAAc;cAaJ,oBAAqB,QAChC,YAAY,qBACD,QAAQ,cAAc,SAAS;KAmEhC;WACD;WACA;WACA;WACA;WACA;;KAGC;EACN;EAAkB;;EAClB;EAAkB;;KAEZ;EACV,UAAU,KAAK;EACf,YAAY,KAAK;EACjB,YAAY,KAAK;;EAEjB,aAAa,KAAK;EAClB,WAAW,KAAK,oBAAoB,SAAS;;KAGnC;EACV;EACA;EACA;EACA,SAAS,cAAc;EACvB;EACA,YAAY,SAAS;;KAGX;EACV,cAAc,UAAU;EACxB,UAAU,MAAM,uBAAuB;;EAEvC;;cAKW,kBAAmB,qBAAmB;cAGtC,mBAAoB,kBAAkB,MAAM;cAkL5C,uBAAwB,kBAAkB,UAAU,UAC/D,WACK,YAAY,SAAS,cAAc,qCAIvC,UAAU,cAAc,cAAc,gBAAgB"}
|
package/dist/host/host.js
CHANGED
|
@@ -26,7 +26,6 @@ const useStatewireState = (init) => {
|
|
|
26
26
|
ctl: {
|
|
27
27
|
apply: (ops) => {
|
|
28
28
|
const result = applyOps(commit(), ops);
|
|
29
|
-
if (result.events.length > 0) throw new Error("statewire: event ops do not apply to state — route them to emit");
|
|
30
29
|
c.current = result.state;
|
|
31
30
|
c.draft?.invalidate();
|
|
32
31
|
c.changed.push(...result.changedPaths);
|
|
@@ -82,20 +81,24 @@ const LOCAL_CALLER = Object.freeze({
|
|
|
82
81
|
context: null,
|
|
83
82
|
isConnected: true
|
|
84
83
|
});
|
|
85
|
-
const toSendError = (error) => error instanceof StatewireSendError ? error : new StatewireSendError({
|
|
84
|
+
const toSendError = (error) => error instanceof StatewireSendError ? error : error instanceof StatewireReject ? new StatewireSendError({
|
|
86
85
|
fate: "rejected",
|
|
87
86
|
cause: "server",
|
|
88
|
-
message: error
|
|
89
|
-
...error
|
|
87
|
+
message: error.message,
|
|
88
|
+
...error.payload !== void 0 && { payload: error.payload }
|
|
89
|
+
}) : new StatewireSendError({
|
|
90
|
+
fate: "failed",
|
|
91
|
+
cause: "server",
|
|
92
|
+
message: error instanceof Error ? error.message : String(error)
|
|
90
93
|
});
|
|
91
|
-
const
|
|
92
|
-
if (inv.
|
|
93
|
-
inv.
|
|
94
|
-
cell.listener?.
|
|
95
|
-
inv.origin?.
|
|
94
|
+
const applyInvocation = (cell, inv) => {
|
|
95
|
+
if (inv.applied) return;
|
|
96
|
+
inv.applied = true;
|
|
97
|
+
cell.listener?.onApplied(inv);
|
|
98
|
+
inv.origin?.onApplied?.();
|
|
96
99
|
};
|
|
97
100
|
const settleInvocation = (cell, inv, call, settled) => {
|
|
98
|
-
|
|
101
|
+
applyInvocation(cell, inv);
|
|
99
102
|
if (settled.type === "resolved") {
|
|
100
103
|
const outcome = settled.value === void 0 ? { type: "accepted" } : {
|
|
101
104
|
type: "accepted",
|
|
@@ -120,8 +123,9 @@ const dispatch = (cell, call) => {
|
|
|
120
123
|
const handler = cell.handlers[call.method];
|
|
121
124
|
if (handler === void 0) {
|
|
122
125
|
const error = new StatewireSendError({
|
|
123
|
-
fate: "
|
|
124
|
-
cause: "server"
|
|
126
|
+
fate: "rejected",
|
|
127
|
+
cause: "server",
|
|
128
|
+
code: "unknown-command"
|
|
125
129
|
});
|
|
126
130
|
call.origin?.onSettle?.({
|
|
127
131
|
type: "rejected",
|
|
@@ -135,7 +139,7 @@ const dispatch = (cell, call) => {
|
|
|
135
139
|
params: call.params,
|
|
136
140
|
ts: call.ts,
|
|
137
141
|
seq: ++cell.seq,
|
|
138
|
-
|
|
142
|
+
applied: false,
|
|
139
143
|
...call.origin !== void 0 && { origin: call.origin },
|
|
140
144
|
...call.origin?.clientId !== void 0 && { clientId: call.origin.clientId }
|
|
141
145
|
};
|
|
@@ -144,7 +148,7 @@ const dispatch = (cell, call) => {
|
|
|
144
148
|
cell.listener?.onCovered?.(inv);
|
|
145
149
|
const ctx = {
|
|
146
150
|
caller: call.origin?.caller ?? LOCAL_CALLER,
|
|
147
|
-
|
|
151
|
+
applied: () => applyInvocation(cell, inv)
|
|
148
152
|
};
|
|
149
153
|
try {
|
|
150
154
|
const value = handler.call(ctx, ...call.params);
|
|
@@ -219,6 +223,7 @@ const useStatewireCommands = (commands) => {
|
|
|
219
223
|
setListener: (listener) => {
|
|
220
224
|
cell.listener = listener;
|
|
221
225
|
},
|
|
226
|
+
methods: () => cell.handlers ? Object.keys(cell.handlers) : [],
|
|
222
227
|
submit: (origin) => new Promise((resolve_0, reject_0) => {
|
|
223
228
|
if (cell.unmounted) {
|
|
224
229
|
reject_0(/* @__PURE__ */ new Error("statewire: commands called after unmount"));
|
|
@@ -259,29 +264,15 @@ const useStatewireCommands = (commands) => {
|
|
|
259
264
|
};
|
|
260
265
|
}, [built]);
|
|
261
266
|
useEffect(() => {
|
|
267
|
+
if (commands) {
|
|
268
|
+
for (const method_0 of Object.keys(commands)) if (method_0.startsWith("statewire/")) throw new Error(`statewire: cannot register "${method_0}": the "statewire/" method namespace is reserved`);
|
|
269
|
+
}
|
|
262
270
|
built.cell.handlers = commands || null;
|
|
263
271
|
pump(built.cell);
|
|
264
272
|
});
|
|
265
273
|
return built.proxy;
|
|
266
274
|
};
|
|
267
|
-
const emitHosts = /* @__PURE__ */ new WeakMap();
|
|
268
|
-
const getEmitHost = (emit) => emit && emitHosts.get(emit);
|
|
269
|
-
const useStatewireEmit = () => {
|
|
270
|
-
const [built] = useState(_temp);
|
|
271
|
-
return built;
|
|
272
|
-
};
|
|
273
|
-
function _temp() {
|
|
274
|
-
const cell = { sink: null };
|
|
275
|
-
const emit = (event) => {
|
|
276
|
-
if (!cell.sink) throw new Error("statewire: emit before a host bound the event sink");
|
|
277
|
-
cell.sink(sanitizeValue(event));
|
|
278
|
-
};
|
|
279
|
-
emitHosts.set(emit, { setSink: (sink) => {
|
|
280
|
-
cell.sink = sink;
|
|
281
|
-
} });
|
|
282
|
-
return emit;
|
|
283
|
-
}
|
|
284
275
|
//#endregion
|
|
285
|
-
export { bindCommandsHost, getCommandsHost,
|
|
276
|
+
export { bindCommandsHost, getCommandsHost, getStateHost, useStatewireCommands, useStatewireState };
|
|
286
277
|
|
|
287
278
|
//# sourceMappingURL=host.js.map
|