statewire 0.0.1 → 0.0.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 +14 -13
- package/dist/client/changes.d.ts +2 -2
- package/dist/client/changes.d.ts.map +1 -1
- package/dist/client/changes.js +3 -10
- package/dist/client/changes.js.map +1 -1
- package/dist/client/connection.d.ts +18 -39
- package/dist/client/connection.d.ts.map +1 -1
- package/dist/client/connection.js +61 -54
- package/dist/client/connection.js.map +1 -1
- package/dist/client/core.d.ts +7 -11
- package/dist/client/core.d.ts.map +1 -1
- package/dist/client/core.js +58 -55
- package/dist/client/core.js.map +1 -1
- package/dist/client/session.d.ts +8 -25
- package/dist/client/session.d.ts.map +1 -1
- package/dist/client/session.js +56 -36
- package/dist/client/session.js.map +1 -1
- package/dist/client/sessionStore.d.ts +1 -1
- package/dist/client/sessionStore.js.map +1 -1
- package/dist/client/timing.d.ts +2 -1
- package/dist/client/timing.d.ts.map +1 -1
- package/dist/client/timing.js +5 -1
- package/dist/client/timing.js.map +1 -1
- package/dist/host/adapter.d.ts +8 -0
- package/dist/host/adapter.d.ts.map +1 -0
- package/dist/host/adapter.js +23 -0
- package/dist/host/adapter.js.map +1 -0
- package/dist/host/draft.d.ts +21 -7
- package/dist/host/draft.d.ts.map +1 -1
- package/dist/host/draft.js +190 -103
- package/dist/host/draft.js.map +1 -1
- package/dist/host/host.d.ts +18 -35
- package/dist/host/host.d.ts.map +1 -1
- package/dist/host/host.js +81 -53
- package/dist/host/host.js.map +1 -1
- package/dist/host/socket.d.ts +14 -0
- package/dist/host/socket.d.ts.map +1 -0
- package/dist/host/socket.js +459 -0
- package/dist/host/socket.js.map +1 -0
- package/dist/host-internal.d.ts +27 -0
- package/dist/host-internal.d.ts.map +1 -0
- package/dist/host-internal.js +3 -0
- package/dist/host.d.ts +44 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +4 -0
- package/dist/index.d.ts +5 -28
- package/dist/index.js +2 -3
- package/dist/statewire.d.ts +94 -10
- package/dist/statewire.d.ts.map +1 -1
- package/dist/statewire.js +23 -35
- package/dist/statewire.js.map +1 -1
- package/dist/transports/fake-sse.d.ts +12 -0
- package/dist/transports/fake-sse.d.ts.map +1 -0
- package/dist/transports/fake-sse.js +39 -0
- package/dist/transports/fake-sse.js.map +1 -0
- package/dist/transports/fixtures.d.ts +33 -0
- package/dist/transports/fixtures.d.ts.map +1 -0
- package/dist/transports/fixtures.js +50 -0
- package/dist/transports/fixtures.js.map +1 -0
- package/dist/transports/remote.d.ts +6 -13
- package/dist/transports/remote.d.ts.map +1 -1
- package/dist/transports/remote.js +5 -24
- package/dist/transports/remote.js.map +1 -1
- package/dist/transports/sse.d.ts +10 -20
- package/dist/transports/sse.d.ts.map +1 -1
- package/dist/transports/sse.js +42 -41
- package/dist/transports/sse.js.map +1 -1
- package/dist/transports/testing.d.ts +4 -3
- package/dist/transports/testing.d.ts.map +1 -1
- package/dist/transports/testing.js +19 -46
- package/dist/transports/testing.js.map +1 -1
- package/dist/transports/ws.d.ts +4 -13
- package/dist/transports/ws.d.ts.map +1 -1
- package/dist/transports/ws.js +57 -60
- package/dist/transports/ws.js.map +1 -1
- package/dist/types.d.ts +53 -100
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/util/draftTraps.d.ts +6 -0
- package/dist/util/draftTraps.d.ts.map +1 -0
- package/dist/util/draftTraps.js +25 -0
- package/dist/util/draftTraps.js.map +1 -0
- package/dist/util/macrotask.d.ts +5 -0
- package/dist/util/macrotask.d.ts.map +1 -0
- package/dist/util/macrotask.js +25 -0
- package/dist/util/macrotask.js.map +1 -0
- package/dist/util/methodProxy.d.ts +5 -0
- package/dist/util/methodProxy.d.ts.map +1 -0
- package/dist/util/methodProxy.js +17 -0
- package/dist/util/methodProxy.js.map +1 -0
- package/dist/util/randomId.d.ts +5 -0
- package/dist/util/randomId.d.ts.map +1 -0
- package/dist/util/randomId.js +6 -0
- package/dist/util/randomId.js.map +1 -0
- package/dist/{client/optimistic.d.ts → util/recordMutations.d.ts} +2 -2
- package/dist/util/recordMutations.d.ts.map +1 -0
- package/dist/{client/optimistic.js → util/recordMutations.js} +5 -24
- package/dist/util/recordMutations.js.map +1 -0
- package/dist/util/statePath.d.ts +14 -0
- package/dist/util/statePath.d.ts.map +1 -0
- package/dist/util/statePath.js +31 -0
- package/dist/util/statePath.js.map +1 -0
- package/dist/wire/meta.d.ts +6 -1
- package/dist/wire/meta.d.ts.map +1 -1
- package/dist/wire/meta.js +30 -28
- package/dist/wire/meta.js.map +1 -1
- package/dist/wire/ops.d.ts +5 -26
- package/dist/wire/ops.d.ts.map +1 -1
- package/dist/wire/ops.js +67 -80
- package/dist/wire/ops.js.map +1 -1
- package/package.json +15 -11
- package/src/client/changes.ts +5 -15
- package/src/client/connection.ts +87 -113
- package/src/client/core.ts +57 -91
- package/src/client/session.ts +87 -70
- package/src/client/sessionStore.ts +1 -1
- package/src/client/timing.ts +6 -0
- package/src/host/adapter.ts +33 -0
- package/src/host/draft.ts +293 -125
- package/src/host/host.ts +107 -106
- package/src/host/socket.ts +542 -0
- package/src/host-internal.ts +28 -0
- package/src/host.ts +61 -0
- package/src/index.ts +2 -62
- package/src/statewire.ts +154 -61
- package/src/transports/fake-sse.ts +51 -0
- package/src/transports/fixtures.ts +60 -0
- package/src/transports/remote.ts +12 -54
- package/src/transports/sse.ts +68 -81
- package/src/transports/testing.ts +27 -59
- package/src/transports/ws.ts +76 -92
- package/src/types.ts +50 -131
- package/src/util/draftTraps.ts +32 -0
- package/src/util/macrotask.ts +25 -0
- package/src/util/methodProxy.ts +16 -0
- package/src/util/randomId.ts +4 -0
- package/src/{client/optimistic.ts → util/recordMutations.ts} +0 -0
- package/src/util/statePath.ts +61 -0
- package/src/wire/meta.ts +42 -47
- package/src/wire/ops.ts +107 -106
- package/LICENSE +0 -21
- package/dist/client/optimistic.d.ts.map +0 -1
- package/dist/client/optimistic.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/server.d.ts +0 -15
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js +0 -3
- package/src/server.ts +0 -32
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws.js","names":["flushTapSync","resource","ResourceElement","encodeParams","timing","createGate","handleEnvelope","overloadedError","reconnectConn","ConnBase","StatewireEnvelope","StatewireTransport","useStatewireRemote","StatewireSessionStore","StatewireCommands","RemoteLink","StatewireSubmitResult","StatewireConnection","StatewireTransportProps","StatewireWireCommand","StatewireWSOptions","url","protocols","query","Record","Promise","webSocket","WebSocket","onChange","connection","sessionStore","Latest","options","ctx","PendingCommand","seq","resolve","result","reject","error","Error","Conn","socket","pending","latest","current","send","OPEN","WS_SUBPROTOCOL","createConn","notify","c","hasSnapshot","lease","attempt","episode","abort","gate","lifetime","reconnect","attemptOnce","attemptSocket","args","sendCommand","status","socketUrl","params","URLSearchParams","set","clientId","base","replace","toString","settleOverloaded","retryAfterMs","undefined","entry","find","p","filter","resolveReceipts","ack","shift","type","resolveResponded","seqs","Set","remaining","has","push","failPending","runSocket","ws","AbortController","timer","ReturnType","setTimeout","timedOut","outcome","closed","attached","arm","clearTimeout","close","heartbeatTimeoutMs","signal","addEventListener","onerror","stale","Boolean","aborted","fail","processEnv","env","done","res","map","r","onmessage","event","frame","overloaded","JSON","parse","String","data","onclose","throwIfAborted","WS","globalThis","extra","command","readyState","stringify","method","useStatewireWS","StatewireWS","TState","TCommands","TEvent"],"sources":["../../src/transports/ws.ts"],"sourcesContent":["import { flushTapSync, resource } from \"@assistant-ui/tap\";\nimport type { ResourceElement } from \"@assistant-ui/tap\";\nimport { encodeParams } from \"../wire/meta\";\nimport { timing } from \"../client/timing\";\nimport {\n createGate,\n handleEnvelope,\n overloadedError,\n reconnectConn,\n} from \"../client/connection\";\nimport type { ConnBase, StatewireEnvelope } from \"../client/connection\";\nimport type { StatewireTransport } from \"../types\";\nimport { useStatewireRemote } from \"./remote\";\nimport type { StatewireSessionStore } from \"../client/sessionStore\";\nimport type { StatewireCommands } from \"../types\";\nimport type {\n RemoteLink,\n StatewireSubmitResult,\n StatewireConnection,\n StatewireTransportProps,\n StatewireWireCommand,\n} from \"./sse\";\n\nexport type StatewireWSOptions = {\n url: string;\n protocols?: string | string[];\n query?:\n | Record<string, string>\n | (() => Record<string, string> | Promise<Record<string, string>>);\n webSocket?: new (url: string, protocols?: string | string[]) => WebSocket;\n onChange?: (connection: StatewireConnection) => void;\n sessionStore?: StatewireSessionStore;\n};\n\ntype Latest = {\n options: StatewireWSOptions;\n ctx: StatewireTransportProps;\n};\n\ntype PendingCommand = {\n seq: number;\n resolve: (result: StatewireSubmitResult) => void;\n reject: (error: Error) => void;\n};\n\ntype Conn = ConnBase & {\n socket: WebSocket | null;\n pending: PendingCommand[];\n latest: { readonly current: Latest };\n send: RemoteLink[\"send\"];\n};\n\nconst OPEN = 1;\n\nexport const WS_SUBPROTOCOL = \"statewire.v1\";\n\nconst createConn = (\n latest: { readonly current: Latest },\n notify: (connection: StatewireConnection) => void,\n): Conn => {\n const c: Conn = {\n hasSnapshot: false,\n lease: null,\n attempt: 0,\n episode: null,\n abort: null,\n socket: null,\n pending: [],\n gate: createGate(),\n lifetime: null,\n latest,\n notify,\n reconnect: () => reconnectConn(c),\n attemptOnce: (abort) => attemptSocket(c, abort),\n send: (args) => sendCommand(c, args),\n connection: { status: \"connecting\", reconnect: () => c.reconnect() },\n };\n return c;\n};\n\nconst socketUrl = async (c: Conn) => {\n const { url, query } = c.latest.current.options;\n const params = new URLSearchParams(\n typeof query === \"function\" ? await query() : query,\n );\n params.set(\"client\", c.latest.current.ctx.clientId);\n const base = url.replace(/\\/$/, \"\").replace(/^http/, \"ws\");\n return `${base}/ws?${params.toString()}`;\n};\n\nconst settleOverloaded = (\n c: Conn,\n { seq, retryAfterMs }: { seq?: number; retryAfterMs?: number },\n) => {\n if (seq === undefined) throw overloadedError(retryAfterMs);\n const entry = c.pending.find((p) => p.seq === seq);\n if (!entry) return;\n c.pending = c.pending.filter((p) => p !== entry);\n entry.reject(overloadedError(retryAfterMs));\n};\n\nconst resolveReceipts = (c: Conn, ack: number) => {\n while (c.pending[0] && c.pending[0].seq <= ack) {\n c.pending.shift()!.resolve({ type: \"receipt\" });\n }\n};\n\nconst resolveResponded = (c: Conn, seqs: Set<number>) => {\n const remaining: PendingCommand[] = [];\n for (const entry of c.pending) {\n if (seqs.has(entry.seq)) entry.resolve({ type: \"receipt\" });\n else remaining.push(entry);\n }\n c.pending = remaining;\n};\n\nconst failPending = (c: Conn, error: Error) => {\n const pending = c.pending;\n c.pending = [];\n for (const entry of pending) entry.reject(error);\n};\n\nconst runSocket = (\n c: Conn,\n ws: WebSocket,\n abort: AbortController,\n): Promise<\"stop\" | \"reattach\"> =>\n new Promise((resolve, reject) => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n let timedOut = false;\n let outcome: \"stop\" | \"reattach\" | null = null;\n let closed = false;\n let attached = false;\n const arm = () => {\n clearTimeout(timer);\n timer = setTimeout(() => {\n timedOut = true;\n ws.close();\n }, timing.heartbeatTimeoutMs);\n };\n arm();\n abort.signal.addEventListener(\"abort\", () => ws.close());\n ws.onerror = () => {};\n const stale = () => Boolean(outcome) || closed || abort.signal.aborted;\n const fail = (error: unknown) => {\n clearTimeout(timer);\n reject(error instanceof Error ? error : new Error(\"protocol\"));\n ws.close();\n };\n const processEnv = (env: StatewireEnvelope) => {\n const done = flushTapSync(() => handleEnvelope(c, env, attached));\n attached = true;\n if (env.ack !== undefined) resolveReceipts(c, env.ack);\n if (env.res !== undefined) {\n resolveResponded(c, new Set(env.res.map((r) => r.seq)));\n }\n if (done) {\n outcome = done;\n ws.close();\n }\n };\n ws.onmessage = (event) => {\n if (stale()) return;\n arm();\n let frame: StatewireEnvelope & {\n overloaded?: { seq?: number; retryAfterMs?: number };\n };\n try {\n frame = JSON.parse(String(event.data)) as typeof frame;\n } catch {\n fail(new Error(\"protocol\"));\n return;\n }\n try {\n if (frame.overloaded !== undefined) {\n settleOverloaded(c, frame.overloaded);\n } else {\n processEnv(frame);\n }\n } catch (error) {\n fail(error);\n }\n };\n ws.onclose = () => {\n closed = true;\n clearTimeout(timer);\n if (c.socket === ws) failPending(c, new Error(\"network\"));\n if (outcome) resolve(outcome);\n else reject(new Error(timedOut ? \"timeout\" : \"network\"));\n };\n });\n\nconst attemptSocket = async (\n c: Conn,\n abort: AbortController,\n): Promise<\"stop\" | \"reattach\"> => {\n let ws: WebSocket | null = null;\n try {\n const url = await socketUrl(c);\n abort.signal.throwIfAborted();\n const { protocols, webSocket } = c.latest.current.options;\n const WS = webSocket ?? globalThis.WebSocket;\n if (!WS) throw new Error(\"no WebSocket implementation\");\n const extra =\n protocols === undefined\n ? []\n : typeof protocols === \"string\"\n ? [protocols]\n : protocols;\n ws = new WS(url, [WS_SUBPROTOCOL, ...extra]);\n c.socket = ws;\n return await runSocket(c, ws, abort);\n } finally {\n if (c.socket === ws) c.socket = null;\n }\n};\n\nconst sendCommand = (\n c: Conn,\n { seq, command }: { seq: number; command: StatewireWireCommand },\n): Promise<StatewireSubmitResult> => {\n const ws = c.socket;\n if (!ws || ws.readyState !== OPEN) {\n return Promise.reject(new Error(\"network\"));\n }\n return new Promise((resolve, reject) => {\n const entry: PendingCommand = {\n seq,\n resolve: (result) => {\n clearTimeout(timer);\n resolve(result);\n },\n reject: (error) => {\n clearTimeout(timer);\n reject(error);\n },\n };\n // A timed-out command fails the socket: the retry then rides reconnect +\n // attach-seq arbitration.\n const timer = setTimeout(() => {\n c.pending = c.pending.filter((p) => p !== entry);\n reject(new Error(\"timeout\"));\n ws.close();\n }, timing.heartbeatTimeoutMs);\n c.pending.push(entry);\n try {\n ws.send(\n JSON.stringify({\n method: command.method,\n ...encodeParams(command.params),\n seq,\n }),\n );\n } catch {\n clearTimeout(timer);\n c.pending = c.pending.filter((p) => p !== entry);\n reject(new Error(\"network\"));\n }\n });\n};\n\nconst useStatewireWS = (\n options: StatewireWSOptions,\n): StatewireTransport<any, any, any> => useStatewireRemote(options, createConn);\n\nexport const StatewireWS = resource(useStatewireWS) as <\n TState = any,\n TCommands extends StatewireCommands = StatewireCommands,\n TEvent = any,\n>(\n options: StatewireWSOptions,\n) => ResourceElement<StatewireTransport<TState | undefined, TCommands, TEvent>>;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ws.js","names":["useState","flushTapSync","resource","ResourceElement","encodeParams","timing","handleEnvelope","overloadedError","useStatewireConn","Conn","StatewireEnvelope","Statewire","StatewireClient","useStatewireRemote","CLIENT_QUERY_PARAM","RemoteLink","StatewireSubmitResult","StatewireRemoteProps","StatewireWireCommand","PendingCommand","seq","resolve","result","reject","error","Error","WsChannel","socket","WebSocket","pending","OPEN","WS_SUBPROTOCOL","socketUrl","options","WSOptions","clientId","url","query","params","URLSearchParams","set","base","replace","toString","settleOverloaded","w","retryAfterMs","undefined","entry","find","p","filter","resolvePending","matches","remaining","type","push","failPending","runSocket","c","ws","abort","AbortController","Promise","timer","ReturnType","setTimeout","timedOut","outcome","closed","arm","clearTimeout","close","heartbeatTimeoutMs","signal","addEventListener","onerror","stale","Boolean","aborted","fail","processEnv","env","done","ack","res","seqs","Set","map","r","has","onmessage","event","frame","overloaded","JSON","parse","String","data","onclose","attemptSocket","ctx","throwIfAborted","protocols","webSocket","WS","globalThis","extra","sendCommand","command","readyState","send","stringify","method","useWsLink","onChange","attemptOnce","link","connection","args","useStatewireWS","StatewireWS","Commands","TState","TCommands","TEvent"],"sources":["../../src/transports/ws.ts"],"sourcesContent":["import { useState } from \"react\";\nimport { flushTapSync, resource } from \"@assistant-ui/tap\";\nimport type { ResourceElement } from \"@assistant-ui/tap\";\nimport { encodeParams } from \"../wire/meta\";\nimport { timing } from \"../client/timing\";\nimport {\n handleEnvelope,\n overloadedError,\n useStatewireConn,\n} from \"../client/connection\";\nimport type { Conn, StatewireEnvelope } from \"../client/connection\";\nimport type { Statewire } from \"../types\";\nimport type { StatewireClient } from \"../statewire\";\nimport { useStatewireRemote } from \"./remote\";\nimport { CLIENT_QUERY_PARAM } from \"../host/adapter\";\nimport type {\n RemoteLink,\n StatewireSubmitResult,\n StatewireRemoteProps,\n StatewireWireCommand,\n} from \"./sse\";\n\ntype PendingCommand = {\n seq: number;\n resolve: (result: StatewireSubmitResult) => void;\n reject: (error: Error) => void;\n};\n\ntype WsChannel = {\n socket: WebSocket | null;\n pending: PendingCommand[];\n};\n\nconst OPEN = 1;\n\nexport const WS_SUBPROTOCOL = \"statewire.v1\";\n\nconst socketUrl = async (\n options: StatewireClient.WSOptions,\n clientId: string,\n) => {\n const { url, query } = options;\n const params = new URLSearchParams(\n typeof query === \"function\" ? await query() : query,\n );\n params.set(CLIENT_QUERY_PARAM, clientId);\n const base = url.replace(/\\/$/, \"\").replace(/^http/, \"ws\");\n return `${base}/ws?${params.toString()}`;\n};\n\nconst settleOverloaded = (\n w: WsChannel,\n { seq, retryAfterMs }: { seq?: number; retryAfterMs?: number },\n) => {\n if (seq === undefined) throw overloadedError(retryAfterMs);\n const entry = w.pending.find((p) => p.seq === seq);\n if (!entry) return;\n w.pending = w.pending.filter((p) => p !== entry);\n entry.reject(overloadedError(retryAfterMs));\n};\n\nconst resolvePending = (\n w: WsChannel,\n matches: (entry: PendingCommand) => boolean,\n) => {\n const remaining: PendingCommand[] = [];\n for (const entry of w.pending) {\n if (matches(entry)) entry.resolve({ type: \"receipt\" });\n else remaining.push(entry);\n }\n w.pending = remaining;\n};\n\nconst failPending = (w: WsChannel, error: Error) => {\n const pending = w.pending;\n w.pending = [];\n for (const entry of pending) entry.reject(error);\n};\n\nconst runSocket = (\n c: Conn,\n w: WsChannel,\n ws: WebSocket,\n abort: AbortController,\n): Promise<\"stop\" | \"reattach\"> =>\n new Promise((resolve, reject) => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n let timedOut = false;\n let outcome: \"stop\" | \"reattach\" | null = null;\n let closed = false;\n const arm = () => {\n clearTimeout(timer);\n timer = setTimeout(() => {\n timedOut = true;\n ws.close();\n }, timing.heartbeatTimeoutMs);\n };\n arm();\n abort.signal.addEventListener(\"abort\", () => ws.close());\n ws.onerror = () => {};\n const stale = () => Boolean(outcome) || closed || abort.signal.aborted;\n const fail = (error: unknown) => {\n clearTimeout(timer);\n reject(error instanceof Error ? error : new Error(\"protocol\"));\n ws.close();\n };\n const processEnv = (env: StatewireEnvelope) => {\n const done = flushTapSync(() => handleEnvelope(c, env));\n if (env.ack !== undefined) {\n const ack = env.ack;\n resolvePending(w, (entry) => entry.seq <= ack);\n }\n if (env.res !== undefined) {\n const seqs = new Set(env.res.map((r) => r.seq));\n resolvePending(w, (entry) => seqs.has(entry.seq));\n }\n if (done) {\n outcome = done;\n ws.close();\n }\n };\n ws.onmessage = (event) => {\n if (stale()) return;\n arm();\n let frame: StatewireEnvelope & {\n overloaded?: { seq?: number; retryAfterMs?: number };\n };\n try {\n frame = JSON.parse(String(event.data)) as typeof frame;\n } catch {\n fail(new Error(\"protocol\"));\n return;\n }\n try {\n if (frame.overloaded !== undefined) {\n settleOverloaded(w, frame.overloaded);\n } else {\n processEnv(frame);\n }\n } catch (error) {\n fail(error);\n }\n };\n ws.onclose = () => {\n closed = true;\n clearTimeout(timer);\n if (w.socket === ws) failPending(w, new Error(\"network\"));\n if (outcome) resolve(outcome);\n else reject(new Error(timedOut ? \"timeout\" : \"network\"));\n };\n });\n\nconst attemptSocket = async (\n c: Conn,\n w: WsChannel,\n options: StatewireClient.WSOptions,\n abort: AbortController,\n): Promise<\"stop\" | \"reattach\"> => {\n let ws: WebSocket | null = null;\n try {\n const url = await socketUrl(options, c.ctx.clientId);\n abort.signal.throwIfAborted();\n const { protocols, webSocket } = options;\n const WS = webSocket ?? globalThis.WebSocket;\n if (!WS) throw new Error(\"no WebSocket implementation\");\n const extra =\n protocols === undefined\n ? []\n : typeof protocols === \"string\"\n ? [protocols]\n : protocols;\n ws = new WS(url, [WS_SUBPROTOCOL, ...extra]);\n w.socket = ws;\n return await runSocket(c, w, ws, abort);\n } finally {\n if (w.socket === ws) w.socket = null;\n }\n};\n\nconst sendCommand = (\n w: WsChannel,\n { seq, command }: { seq: number; command: StatewireWireCommand },\n): Promise<StatewireSubmitResult> => {\n const ws = w.socket;\n if (!ws || ws.readyState !== OPEN) {\n return Promise.reject(new Error(\"network\"));\n }\n return new Promise((resolve, reject) => {\n const entry: PendingCommand = {\n seq,\n resolve: (result) => {\n clearTimeout(timer);\n resolve(result);\n },\n reject: (error) => {\n clearTimeout(timer);\n reject(error);\n },\n };\n // A timed-out command fails the socket: the retry then rides reconnect +\n // attach-seq arbitration.\n const timer = setTimeout(() => {\n w.pending = w.pending.filter((p) => p !== entry);\n reject(new Error(\"timeout\"));\n ws.close();\n }, timing.heartbeatTimeoutMs);\n w.pending.push(entry);\n try {\n ws.send(\n JSON.stringify({\n method: command.method,\n ...encodeParams(command.params),\n seq,\n }),\n );\n } catch {\n clearTimeout(timer);\n w.pending = w.pending.filter((p) => p !== entry);\n reject(new Error(\"network\"));\n }\n });\n};\n\nconst useWsLink = (\n options: StatewireClient.WSOptions,\n ctx: StatewireRemoteProps,\n): RemoteLink => {\n const [w] = useState((): WsChannel => ({ socket: null, pending: [] }));\n const c = useStatewireConn({\n url: options.url,\n onChange: options.onChange,\n ctx,\n attemptOnce: (c, abort) => attemptSocket(c, w, options, abort),\n });\n const [link] = useState(\n (): RemoteLink => ({\n get connection() {\n return c.connection;\n },\n send: (args) => sendCommand(w, args),\n }),\n );\n return link;\n};\n\nconst useStatewireWS = (\n options: StatewireClient.WSOptions,\n): Statewire<any, any, any> => useStatewireRemote(options, useWsLink);\n\nexport const StatewireWS = resource(useStatewireWS) as <\n TState = any,\n TCommands extends Statewire.Commands = Statewire.Commands,\n TEvent = any,\n>(\n options: StatewireClient.WSOptions,\n) => ResourceElement<Statewire<TState | undefined, TCommands, TEvent>>;\n"],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,107 +1,60 @@
|
|
|
1
|
+
import { StatewireClient } from "./statewire.js";
|
|
1
2
|
import { StatewirePath } from "./wire/ops.js";
|
|
2
|
-
import { StatewireConnection } from "./transports/sse.js";
|
|
3
|
-
import { ResourceElement } from "@assistant-ui/tap";
|
|
4
3
|
//#region src/types.d.ts
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
type
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
type
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
type
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
transport: ResourceElement<StatewireTransport<TState, TCommands, TEvent>>;
|
|
50
|
-
optimistic?: StatewireOptimistic<NonNullable<TState>, TCommands>;
|
|
51
|
-
onEvent?: (event: TEvent, ctx: {
|
|
52
|
-
state: NonNullable<TState>;
|
|
53
|
-
}) => void;
|
|
54
|
-
onStateChange?: (state: NonNullable<TState>, ctx: {
|
|
55
|
-
changes: StatewireChanges;
|
|
56
|
-
}) => void;
|
|
57
|
-
onCommandChange?: (update: StatewireCommandUpdate<TCommands>, ctx: {
|
|
58
|
-
state: NonNullable<TState>;
|
|
59
|
-
}) => void;
|
|
60
|
-
};
|
|
61
|
-
type StatewireHelpers<TState, TCommands extends StatewireCommands = StatewireCommands> = {
|
|
62
|
-
/** The transport's state type — remote transports include `undefined` until the first attach snapshot applies. */
|
|
4
|
+
declare namespace Statewire {
|
|
5
|
+
type Commands = Record<string, (...args: never[]) => unknown>;
|
|
6
|
+
type Command<TCommands extends Commands = Commands> = { [K in keyof TCommands]: {
|
|
7
|
+
method: K;
|
|
8
|
+
params: Parameters<TCommands[K]>;
|
|
9
|
+
}; }[keyof TCommands];
|
|
10
|
+
type CommandsProxy<TCommands extends Commands> = { [K in keyof TCommands]: (...args: Parameters<TCommands[K]>) => Promise<Awaited<ReturnType<TCommands[K]>>>; };
|
|
11
|
+
type CommandUpdate<TCommands extends Commands = Commands> = { [K in keyof TCommands]: {
|
|
12
|
+
method: K;
|
|
13
|
+
params: Parameters<TCommands[K]>;
|
|
14
|
+
seq: number;
|
|
15
|
+
status: "queued" | "posted" | "acked" | "settled";
|
|
16
|
+
result?: unknown;
|
|
17
|
+
failure?: StatewireClient.SendFailure;
|
|
18
|
+
}; }[keyof TCommands];
|
|
19
|
+
/** Push-based canonical state feed a statewire hands to the client core. */
|
|
20
|
+
type StateSource = {
|
|
21
|
+
snapshot: () => unknown;
|
|
22
|
+
/** Drain the paths changed since the last take (`[]` entry = whole state). */
|
|
23
|
+
takeChanged: () => StatewirePath[];
|
|
24
|
+
/** Synchronous, per change; the consumer owns coalescing. */
|
|
25
|
+
setOnChange: (listener: (() => void) | null) => void;
|
|
26
|
+
};
|
|
27
|
+
type Op = {
|
|
28
|
+
op: "replace";
|
|
29
|
+
path: StatewirePath;
|
|
30
|
+
value: unknown;
|
|
31
|
+
} | {
|
|
32
|
+
op: "add";
|
|
33
|
+
path: StatewirePath;
|
|
34
|
+
value: unknown;
|
|
35
|
+
} | {
|
|
36
|
+
op: "remove";
|
|
37
|
+
path: StatewirePath;
|
|
38
|
+
} | {
|
|
39
|
+
op: "event";
|
|
40
|
+
value: object;
|
|
41
|
+
};
|
|
42
|
+
type Changes = {
|
|
43
|
+
isChangedAt(path: readonly (string | number)[]): boolean;
|
|
44
|
+
getChangedKeys(path: readonly (string | number)[]): string[];
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
interface Statewire<TState = any, TCommands extends Statewire.Commands = Statewire.Commands, TEvent = any> {
|
|
63
48
|
state: TState;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
* "unknown-command" = no such command, "invalid-params" = the args did not
|
|
70
|
-
* match the command's schema, "lost" = sent but never covered by an ack,
|
|
71
|
-
* "crashed" = acked but the processing died, "result-unavailable" = acked
|
|
72
|
-
* but the result is gone.
|
|
73
|
-
* cause is which channel settled it: "server" = the server's answer;
|
|
74
|
-
* the rest are channel deaths the client settled locally.
|
|
75
|
-
*/
|
|
76
|
-
type SendFailure = {
|
|
77
|
-
fate: "rejected";
|
|
78
|
-
cause: "server";
|
|
79
|
-
message: string;
|
|
80
|
-
payload?: unknown;
|
|
81
|
-
} | {
|
|
82
|
-
fate: "unknown-command";
|
|
83
|
-
cause: "server";
|
|
84
|
-
} | {
|
|
85
|
-
fate: "invalid-params";
|
|
86
|
-
cause: "server";
|
|
87
|
-
message: string;
|
|
88
|
-
} | {
|
|
89
|
-
fate: "crashed";
|
|
90
|
-
cause: "server";
|
|
91
|
-
} | {
|
|
92
|
-
fate: "result-unavailable";
|
|
93
|
-
cause: "server" | "unmount" | "stream-gone" | "session-lost" | "superseded";
|
|
94
|
-
} | {
|
|
95
|
-
fate: "aborted";
|
|
96
|
-
cause: "cascade" | "unmount" | "stream-gone" | "session-lost" | "superseded";
|
|
97
|
-
} | {
|
|
98
|
-
fate: "lost";
|
|
99
|
-
cause: "unmount" | "stream-gone" | "session-lost" | "superseded";
|
|
100
|
-
};
|
|
49
|
+
commands: Statewire.CommandsProxy<TCommands>;
|
|
50
|
+
emit?: (event: TEvent) => void;
|
|
51
|
+
connection?: StatewireClient.Connection;
|
|
52
|
+
source?: Statewire.StateSource;
|
|
53
|
+
}
|
|
101
54
|
declare class StatewireSendError extends Error {
|
|
102
|
-
readonly failure: SendFailure;
|
|
103
|
-
constructor(failure: SendFailure);
|
|
55
|
+
readonly failure: StatewireClient.SendFailure;
|
|
56
|
+
constructor(failure: StatewireClient.SendFailure);
|
|
104
57
|
}
|
|
105
58
|
//#endregion
|
|
106
|
-
export {
|
|
59
|
+
export { Statewire, StatewireSendError };
|
|
107
60
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"mappings":";;;kBAGiB;OACH,WAAW,mBAAmB;OAE9B,QAAQ,kBAAkB,WAAW,eAC9C,WAAW;IAAc,QAAQ;IAAG,QAAQ,WAAW,UAAU;aAC5D;OAEI,cAAc,kBAAkB,eACzC,WAAW,gBACP,MAAM,WAAW,UAAU,QAC3B,QAAQ,QAAQ,WAAW,UAAU;OAGhC,cAAc,kBAAkB,WAAW,eACpD,WAAW;IACV,QAAQ;IACR,QAAQ,WAAW,UAAU;IAC7B;IACA;IACA;IACA,UAAU,gBAAgB;aAEtB;;OAGI;IACV;;IAEA,mBAAmB;;IAEnB,cAAc;;OAGJ;IACN;IAAe,MAAM;IAAe;;IACpC;IAAW,MAAM;IAAe;;IAChC;IAAc,MAAM;;IACpB;IAAa;;OAEP;IACV,YAAY;IACZ,eAAe;;;UAIF,UACf,cACA,kBAAkB,UAAU,WAAW,UAAU,UACjD;EAEA,OAAO;EACP,UAAU,UAAU,cAAc;EAClC,QAAQ,OAAO;EACf,aAAa,gBAAgB;EAC7B,SAAS,UAAU;;cAsBR,2BAA2B;WAC7B,SAAS,gBAAgB;EAEtB,YAAA,SAAS,gBAAgB"}
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","names":["
|
|
1
|
+
{"version":3,"file":"types.js","names":["StatewireClient","StatewirePath","Statewire","Commands","Record","args","Command","TCommands","method","K","params","Parameters","CommandsProxy","Promise","Awaited","ReturnType","CommandUpdate","seq","status","result","failure","SendFailure","StateSource","snapshot","takeChanged","setOnChange","listener","Op","op","path","value","Changes","isChangedAt","getChangedKeys","state","TState","commands","emit","event","TEvent","connection","Connection","source","describe","fate","message","cause","StatewireSendError","Error","constructor","name"],"sources":["../src/types.ts"],"sourcesContent":["import type { StatewireClient } from \"./statewire\";\nimport type { StatewirePath } from \"./wire/ops\";\n\nexport namespace Statewire {\n export type Commands = Record<string, (...args: never[]) => unknown>;\n\n export type Command<TCommands extends Commands = Commands> = {\n [K in keyof TCommands]: { method: K; params: Parameters<TCommands[K]> };\n }[keyof TCommands];\n\n export type CommandsProxy<TCommands extends Commands> = {\n [K in keyof TCommands]: (\n ...args: Parameters<TCommands[K]>\n ) => Promise<Awaited<ReturnType<TCommands[K]>>>;\n };\n\n export type CommandUpdate<TCommands extends Commands = Commands> = {\n [K in keyof TCommands]: {\n method: K;\n params: Parameters<TCommands[K]>;\n seq: number;\n status: \"queued\" | \"posted\" | \"acked\" | \"settled\";\n result?: unknown;\n failure?: StatewireClient.SendFailure;\n };\n }[keyof TCommands];\n\n /** Push-based canonical state feed a statewire hands to the client core. */\n export type StateSource = {\n snapshot: () => unknown;\n /** Drain the paths changed since the last take (`[]` entry = whole state). */\n takeChanged: () => StatewirePath[];\n /** Synchronous, per change; the consumer owns coalescing. */\n setOnChange: (listener: (() => void) | null) => void;\n };\n\n export type Op =\n | { op: \"replace\"; path: StatewirePath; value: unknown }\n | { op: \"add\"; path: StatewirePath; value: unknown }\n | { op: \"remove\"; path: StatewirePath }\n | { op: \"event\"; value: object };\n\n export type Changes = {\n isChangedAt(path: readonly (string | number)[]): boolean;\n getChangedKeys(path: readonly (string | number)[]): string[];\n };\n}\n\nexport interface Statewire<\n TState = any,\n TCommands extends Statewire.Commands = Statewire.Commands,\n TEvent = any,\n> {\n state: TState;\n commands: Statewire.CommandsProxy<TCommands>;\n emit?: (event: TEvent) => void;\n connection?: StatewireClient.Connection;\n source?: Statewire.StateSource;\n}\n\nconst describe = (failure: StatewireClient.SendFailure): string => {\n switch (failure.fate) {\n case \"rejected\":\n return `statewire: command rejected: ${failure.message}`;\n case \"unknown-command\":\n return \"statewire: unknown command\";\n case \"invalid-params\":\n return `statewire: invalid params: ${failure.message}`;\n case \"aborted\":\n return `statewire: command aborted (${failure.cause})`;\n case \"lost\":\n return `statewire: command lost (${failure.cause})`;\n case \"crashed\":\n return \"statewire: command crashed\";\n case \"result-unavailable\":\n return `statewire: command result unavailable (${failure.cause})`;\n }\n};\n\nexport class StatewireSendError extends Error {\n readonly failure: StatewireClient.SendFailure;\n\n constructor(failure: StatewireClient.SendFailure) {\n super(describe(failure));\n this.name = \"StatewireSendError\";\n this.failure = failure;\n }\n}\n"],"mappings":";AA4DA,MAAM2C,YAAYvB,YAAiD;CACjE,QAAQA,QAAQwB,MAAhB;EACE,KAAK,YACH,OAAO,gCAAgCxB,QAAQyB;EACjD,KAAK,mBACH,OAAO;EACT,KAAK,kBACH,OAAO,8BAA8BzB,QAAQyB;EAC/C,KAAK,WACH,OAAO,+BAA+BzB,QAAQ0B,MAAK;EACrD,KAAK,QACH,OAAO,4BAA4B1B,QAAQ0B,MAAK;EAClD,KAAK,WACH,OAAO;EACT,KAAK,sBACH,OAAO,0CAA0C1B,QAAQ0B,MAAK;CAClE;AACF;AAEA,IAAaC,qBAAb,cAAwCC,MAAM;CAC5C;CAEAC,YAAY7B,SAAsC;EAChD,MAAMuB,SAASvB,OAAO,CAAC;EACvB,KAAK8B,OAAO;EACZ,KAAK9B,UAAUA;CACjB;AACF"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
//#region src/util/draftTraps.d.ts
|
|
2
|
+
type SharedTraps = Pick<ProxyHandler<object>, "defineProperty" | "setPrototypeOf" | "preventExtensions" | "ownKeys" | "getOwnPropertyDescriptor">;
|
|
3
|
+
declare const sharedDraftTraps: (current: () => object, context: string) => SharedTraps;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { sharedDraftTraps };
|
|
6
|
+
//# sourceMappingURL=draftTraps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draftTraps.d.ts","names":[],"sources":["../../src/util/draftTraps.ts"],"mappings":";KAAK,cAAc,KACjB;cAQW,mBACX,uBACA,oBACC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/util/draftTraps.ts
|
|
2
|
+
const sharedDraftTraps = (current, context) => {
|
|
3
|
+
const unsupported = (op) => {
|
|
4
|
+
throw new Error(`statewire: ${op} is not supported ${context}`);
|
|
5
|
+
};
|
|
6
|
+
return {
|
|
7
|
+
defineProperty: () => unsupported("defineProperty"),
|
|
8
|
+
setPrototypeOf: () => unsupported("setPrototypeOf"),
|
|
9
|
+
preventExtensions: () => unsupported("preventExtensions"),
|
|
10
|
+
ownKeys: () => Reflect.ownKeys(current()),
|
|
11
|
+
getOwnPropertyDescriptor: (_target, prop) => {
|
|
12
|
+
const node = current();
|
|
13
|
+
const desc = Reflect.getOwnPropertyDescriptor(node, prop);
|
|
14
|
+
if (!desc) return void 0;
|
|
15
|
+
return {
|
|
16
|
+
...desc,
|
|
17
|
+
configurable: !Array.isArray(node) || prop !== "length"
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
//#endregion
|
|
23
|
+
export { sharedDraftTraps };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=draftTraps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draftTraps.js","names":["SharedTraps","Pick","ProxyHandler","sharedDraftTraps","current","context","unsupported","op","Error","defineProperty","setPrototypeOf","preventExtensions","ownKeys","Reflect","getOwnPropertyDescriptor","_target","prop","node","desc","undefined","configurable","Array","isArray"],"sources":["../../src/util/draftTraps.ts"],"sourcesContent":["type SharedTraps = Pick<\n ProxyHandler<object>,\n | \"defineProperty\"\n | \"setPrototypeOf\"\n | \"preventExtensions\"\n | \"ownKeys\"\n | \"getOwnPropertyDescriptor\"\n>;\n\nexport const sharedDraftTraps = (\n current: () => object,\n context: string,\n): SharedTraps => {\n const unsupported = (op: string): never => {\n throw new Error(`statewire: ${op} is not supported ${context}`);\n };\n return {\n defineProperty: () => unsupported(\"defineProperty\"),\n setPrototypeOf: () => unsupported(\"setPrototypeOf\"),\n preventExtensions: () => unsupported(\"preventExtensions\"),\n ownKeys: () => Reflect.ownKeys(current()),\n getOwnPropertyDescriptor: (_target, prop) => {\n const node = current();\n const desc = Reflect.getOwnPropertyDescriptor(node, prop);\n if (!desc) return undefined;\n return {\n ...desc,\n configurable: !Array.isArray(node) || prop !== \"length\",\n };\n },\n };\n};\n"],"mappings":";AASA,MAAaG,oBACXC,SACAC,YACgB;CAChB,MAAMC,eAAeC,OAAsB;EACzC,MAAM,IAAIC,MAAM,cAAcD,GAAE,oBAAqBF,SAAS;CAChE;CACA,OAAO;EACLI,sBAAsBH,YAAY,gBAAgB;EAClDI,sBAAsBJ,YAAY,gBAAgB;EAClDK,yBAAyBL,YAAY,mBAAmB;EACxDM,eAAeC,QAAQD,QAAQR,QAAQ,CAAC;EACxCU,2BAA2BC,SAASC,SAAS;GAC3C,MAAMC,OAAOb,QAAQ;GACrB,MAAMc,OAAOL,QAAQC,yBAAyBG,MAAMD,IAAI;GACxD,IAAI,CAACE,MAAM,OAAOC,KAAAA;GAClB,OAAO;IACL,GAAGD;IACHE,cAAc,CAACC,MAAMC,QAAQL,IAAI,KAAKD,SAAS;GACjD;EACF;CACF;AACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"macrotask.d.ts","names":[],"sources":["../../src/util/macrotask.ts"],"mappings":";cAIa,wBAAyB"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/util/macrotask.ts
|
|
2
|
+
const createMacrotaskPoster = (callback) => {
|
|
3
|
+
if (typeof MessageChannel !== "undefined") {
|
|
4
|
+
let port1 = null;
|
|
5
|
+
let port2;
|
|
6
|
+
return () => {
|
|
7
|
+
if (!port1) {
|
|
8
|
+
const channel = new MessageChannel();
|
|
9
|
+
channel.port1.onmessage = () => {
|
|
10
|
+
port1?.unref?.();
|
|
11
|
+
callback();
|
|
12
|
+
};
|
|
13
|
+
port1 = channel.port1;
|
|
14
|
+
port2 = channel.port2;
|
|
15
|
+
}
|
|
16
|
+
port1.ref?.();
|
|
17
|
+
port2.postMessage(null);
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
return () => setTimeout(callback, 0);
|
|
21
|
+
};
|
|
22
|
+
//#endregion
|
|
23
|
+
export { createMacrotaskPoster };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=macrotask.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"macrotask.js","names":["createMacrotaskPoster","callback","MessageChannel","port1","MessagePort","ref","unref","port2","channel","onmessage","postMessage","setTimeout"],"sources":["../../src/util/macrotask.ts"],"sourcesContent":["// MessageChannel schedules the flush as a macrotask (tap's pattern): it runs\n// after the whole microtask drain, so every producer in the current turn\n// coalesces. The port is ref'd only while a flush is pending — an active\n// MessagePort holds the Node event loop open. ref/unref are Node-only.\nexport const createMacrotaskPoster = (callback: () => void): (() => void) => {\n if (typeof MessageChannel !== \"undefined\") {\n let port1: (MessagePort & { ref?: () => void; unref?: () => void }) | null =\n null;\n let port2: MessagePort;\n return () => {\n if (!port1) {\n const channel = new MessageChannel();\n channel.port1.onmessage = () => {\n port1?.unref?.();\n callback();\n };\n port1 = channel.port1;\n port2 = channel.port2;\n }\n port1.ref?.();\n port2!.postMessage(null);\n };\n }\n return () => setTimeout(callback, 0);\n};\n"],"mappings":";AAIA,MAAaA,yBAAyBC,aAAuC;CAC3E,IAAI,OAAOC,mBAAmB,aAAa;EACzC,IAAIC,QACF;EACF,IAAII;EACJ,aAAa;GACX,IAAI,CAACJ,OAAO;IACV,MAAMK,UAAU,IAAIN,eAAe;IACnCM,QAAQL,MAAMM,kBAAkB;KAC9BN,OAAOG,QAAQ;KACfL,SAAS;IACX;IACAE,QAAQK,QAAQL;IAChBI,QAAQC,QAAQD;GAClB;GACAJ,MAAME,MAAM;GACZE,MAAOG,YAAY,IAAI;EACzB;CACF;CACA,aAAaC,WAAWV,UAAU,CAAC;AACrC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"methodProxy.d.ts","names":[],"sources":["../../src/util/methodProxy.ts"],"mappings":";cAAa,oBAAqB,kBAChC,WAAW,gBAAgB,sBAAsB,qBAChD"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/util/methodProxy.ts
|
|
2
|
+
const createMethodProxy = (dispatch) => {
|
|
3
|
+
const memo = /* @__PURE__ */ new Map();
|
|
4
|
+
return new Proxy({}, { get: (_target, method) => {
|
|
5
|
+
if (typeof method !== "string") return void 0;
|
|
6
|
+
let fn = memo.get(method);
|
|
7
|
+
if (fn === void 0) {
|
|
8
|
+
fn = (...params) => dispatch(method, params);
|
|
9
|
+
memo.set(method, fn);
|
|
10
|
+
}
|
|
11
|
+
return fn;
|
|
12
|
+
} });
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
export { createMethodProxy };
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=methodProxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"methodProxy.js","names":["createMethodProxy","dispatch","method","params","Promise","T","memo","Map","Proxy","get","_target","undefined","fn","set"],"sources":["../../src/util/methodProxy.ts"],"sourcesContent":["export const createMethodProxy = <T extends object>(\n dispatch: (method: string, params: unknown[]) => Promise<unknown>,\n): T => {\n const memo = new Map<string, (...params: unknown[]) => Promise<unknown>>();\n return new Proxy({} as T, {\n get: (_target, method) => {\n if (typeof method !== \"string\") return undefined;\n let fn = memo.get(method);\n if (fn === undefined) {\n fn = (...params: unknown[]) => dispatch(method, params);\n memo.set(method, fn);\n }\n return fn;\n },\n });\n};\n"],"mappings":";AAAA,MAAaA,qBACXC,aACM;CACN,MAAMK,uBAAO,IAAIC,IAAwD;CACzE,OAAO,IAAIC,MAAM,CAAC,GAAQ,EACxBC,MAAMC,SAASR,WAAW;EACxB,IAAI,OAAOA,WAAW,UAAU,OAAOS,KAAAA;EACvC,IAAIC,KAAKN,KAAKG,IAAIP,MAAM;EACxB,IAAIU,OAAOD,KAAAA,GAAW;GACpBC,MAAM,GAAGT,WAAsBF,SAASC,QAAQC,MAAM;GACtDG,KAAKO,IAAIX,QAAQU,EAAE;EACrB;EACA,OAAOA;CACT,EACF,CAAC;AACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"randomId.d.ts","names":[],"sources":["../../src/util/randomId.ts"],"mappings":";cAAa"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
//#region src/util/randomId.ts
|
|
2
|
+
const randomId = () => typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
3
|
+
//#endregion
|
|
4
|
+
export { randomId };
|
|
5
|
+
|
|
6
|
+
//# sourceMappingURL=randomId.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"randomId.js","names":["randomId","crypto","randomUUID","Date","now","toString","Math","random","slice"],"sources":["../../src/util/randomId.ts"],"sourcesContent":["export const randomId = (): string =>\n typeof crypto !== \"undefined\" && \"randomUUID\" in crypto\n ? crypto.randomUUID()\n : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n"],"mappings":";AAAA,MAAaA,iBACX,OAAOC,WAAW,eAAe,gBAAgBA,SAC7CA,OAAOC,WAAW,IAClB,GAAGC,KAAKC,IAAI,CAAC,CAACC,SAAS,EAAE,EAAC,GAAIC,KAAKC,OAAO,CAAC,CAACF,SAAS,EAAE,CAAC,CAACG,MAAM,GAAG,EAAE"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region src/
|
|
1
|
+
//#region src/util/recordMutations.d.ts
|
|
2
2
|
type PathKey = string | number;
|
|
3
3
|
type Path = readonly PathKey[];
|
|
4
4
|
declare const recordMutations: <T>(base: T, mutate: (draft: T) => void) => {
|
|
@@ -7,4 +7,4 @@ declare const recordMutations: <T>(base: T, mutate: (draft: T) => void) => {
|
|
|
7
7
|
};
|
|
8
8
|
//#endregion
|
|
9
9
|
export { recordMutations };
|
|
10
|
-
//# sourceMappingURL=
|
|
10
|
+
//# sourceMappingURL=recordMutations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recordMutations.d.ts","names":[],"sources":["../../src/util/recordMutations.ts"],"mappings":";KAEK;KACA,gBAAgB;cAuER,kBAAmB,GAC9B,MAAM,GACN,SAAS,OAAO;EACb,OAAO;EAAG,cAAc"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { sharedDraftTraps } from "./draftTraps.js";
|
|
2
|
+
//#region src/util/recordMutations.ts
|
|
2
3
|
const DRAFT = Symbol("statewire.draft");
|
|
3
4
|
const isDraftable = (value) => {
|
|
4
5
|
if (typeof value !== "object" || value === null) return false;
|
|
@@ -34,10 +35,7 @@ const finalize = (node) => {
|
|
|
34
35
|
return result;
|
|
35
36
|
};
|
|
36
37
|
const recordMutations = (base, mutate) => {
|
|
37
|
-
if (!isDraftable(base))
|
|
38
|
-
state: base,
|
|
39
|
-
changedPaths: []
|
|
40
|
-
};
|
|
38
|
+
if (!isDraftable(base)) throw new Error("statewire: cannot record mutations on primitive state");
|
|
41
39
|
const changedPaths = [];
|
|
42
40
|
const seen = /* @__PURE__ */ new Set();
|
|
43
41
|
const record = (path) => {
|
|
@@ -93,25 +91,8 @@ const recordMutations = (base, mutate) => {
|
|
|
93
91
|
delete node.copy[prop];
|
|
94
92
|
return true;
|
|
95
93
|
},
|
|
96
|
-
defineProperty: () => {
|
|
97
|
-
throw new Error("statewire: defineProperty is not supported inside a mutator");
|
|
98
|
-
},
|
|
99
|
-
setPrototypeOf: () => {
|
|
100
|
-
throw new Error("statewire: setPrototypeOf is not supported inside a mutator");
|
|
101
|
-
},
|
|
102
|
-
preventExtensions: () => {
|
|
103
|
-
throw new Error("statewire: preventExtensions is not supported inside a mutator");
|
|
104
|
-
},
|
|
105
94
|
has: (_target, prop) => prop === DRAFT || Reflect.has(latest(node), prop),
|
|
106
|
-
|
|
107
|
-
getOwnPropertyDescriptor: (_target, prop) => {
|
|
108
|
-
const desc = Reflect.getOwnPropertyDescriptor(latest(node), prop);
|
|
109
|
-
if (!desc) return void 0;
|
|
110
|
-
return {
|
|
111
|
-
...desc,
|
|
112
|
-
configurable: !node.isArray || prop !== "length"
|
|
113
|
-
};
|
|
114
|
-
}
|
|
95
|
+
...sharedDraftTraps(() => latest(node), "inside a mutator")
|
|
115
96
|
});
|
|
116
97
|
return node;
|
|
117
98
|
};
|
|
@@ -125,4 +106,4 @@ const recordMutations = (base, mutate) => {
|
|
|
125
106
|
//#endregion
|
|
126
107
|
export { recordMutations };
|
|
127
108
|
|
|
128
|
-
//# sourceMappingURL=
|
|
109
|
+
//# sourceMappingURL=recordMutations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recordMutations.js","names":["sharedDraftTraps","PathKey","Path","DRAFT","Symbol","Node","base","Record","PropertyKey","copy","modified","isArray","parent","path","proxy","assigned","Set","isDraftable","value","Array","proto","Object","getPrototypeOf","prototype","nodeOf","undefined","latest","node","prepareCopy","slice","markModified","n","resolveAssigned","container","key","keys","child","finalize","result","has","recordMutations","T","mutate","draft","state","changedPaths","Error","seen","record","join","add","push","createNode","touched","prop","Proxy","get","_target","current","Number","set","next","source","hasOwn","deleteProperty","Reflect","root"],"sources":["../../src/util/recordMutations.ts"],"sourcesContent":["import { sharedDraftTraps } from \"./draftTraps\";\n\ntype PathKey = string | number;\ntype Path = readonly PathKey[];\n\nconst DRAFT = Symbol(\"statewire.draft\");\n\ntype Node = {\n base: Record<PropertyKey, unknown>;\n copy: Record<PropertyKey, unknown> | null;\n modified: boolean;\n isArray: boolean;\n parent: Node | null;\n path: Path;\n proxy: unknown;\n assigned: Set<PropertyKey> | null;\n};\n\nconst isDraftable = (value: unknown): value is object => {\n if (typeof value !== \"object\" || value === null) return false;\n if (Array.isArray(value)) return true;\n const proto: unknown = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n};\n\nconst nodeOf = (value: unknown): Node | undefined =>\n typeof value === \"object\" && value !== null\n ? (value as { [DRAFT]?: Node })[DRAFT]\n : undefined;\n\nconst latest = (node: Node) => node.copy ?? node.base;\n\nconst prepareCopy = (node: Node) => {\n node.copy ??= node.isArray\n ? ((node.base as unknown as unknown[]).slice() as unknown as Record<\n PropertyKey,\n unknown\n >)\n : { ...node.base };\n};\n\nconst markModified = (node: Node) => {\n for (let n: Node | null = node; n && !n.modified; n = n.parent) {\n n.modified = true;\n }\n};\n\nconst resolveAssigned = (container: object) => {\n for (const key of Object.keys(container)) {\n const value = (container as Record<string, unknown>)[key];\n const child = nodeOf(value);\n if (child) {\n (container as Record<string, unknown>)[key] = finalize(child);\n } else if (isDraftable(value)) {\n resolveAssigned(value);\n }\n }\n};\n\nconst finalize = (node: Node): unknown => {\n if (!node.modified) return node.base;\n const result = node.copy!;\n for (const key of Object.keys(result)) {\n const value = result[key];\n const child = nodeOf(value);\n if (child) {\n result[key] = finalize(child);\n } else if (node.assigned?.has(key) && isDraftable(value)) {\n resolveAssigned(value);\n }\n }\n return result;\n};\n\nexport const recordMutations = <T>(\n base: T,\n mutate: (draft: T) => void,\n): { state: T; changedPaths: Path[] } => {\n if (!isDraftable(base)) {\n throw new Error(\"statewire: cannot record mutations on primitive state\");\n }\n\n const changedPaths: Path[] = [];\n const seen = new Set<string>();\n const record = (path: Path) => {\n const key = path.join(\"\u0000\");\n if (seen.has(key)) return;\n seen.add(key);\n changedPaths.push(path);\n };\n\n const createNode = (value: object, parent: Node | null, path: Path): Node => {\n const node: Node = {\n base: value as Record<PropertyKey, unknown>,\n copy: null,\n modified: false,\n isArray: Array.isArray(value),\n parent,\n path,\n proxy: null,\n assigned: null,\n };\n const touched = (prop: PropertyKey) => {\n if (typeof prop !== \"string\") return;\n record(node.isArray ? node.path : [...node.path, prop]);\n };\n node.proxy = new Proxy(value, {\n get: (_target, prop) => {\n if (prop === DRAFT) return node;\n const current = latest(node)[prop];\n if (typeof prop === \"symbol\") return current;\n if (nodeOf(current)) return current;\n if (isDraftable(current) && current === node.base[prop]) {\n prepareCopy(node);\n const child = createNode(current, node, [\n ...path,\n node.isArray ? Number(prop) : prop,\n ]);\n node.copy![prop] = child.proxy;\n return child.proxy;\n }\n return current;\n },\n set: (_target, prop, next) => {\n const source = latest(node);\n if (Object.hasOwn(source, prop) && source[prop] === next) return true;\n prepareCopy(node);\n markModified(node);\n touched(prop);\n node.copy![prop] = next;\n (node.assigned ??= new Set()).add(prop);\n return true;\n },\n deleteProperty: (_target, prop) => {\n if (!Object.hasOwn(latest(node), prop)) return true;\n prepareCopy(node);\n markModified(node);\n touched(prop);\n delete node.copy![prop];\n return true;\n },\n has: (_target, prop) => prop === DRAFT || Reflect.has(latest(node), prop),\n ...sharedDraftTraps(() => latest(node), \"inside a mutator\"),\n });\n return node;\n };\n\n const root = createNode(base as unknown as object, null, []);\n mutate(root.proxy as T);\n return { state: finalize(root) as T, changedPaths };\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/util/statePath.d.ts
|
|
2
|
+
type StatePath = readonly (string | number)[];
|
|
3
|
+
declare const isUnsafeKey: (key: string | number, parent: string | number | undefined) => boolean;
|
|
4
|
+
type WalkedPath = {
|
|
5
|
+
/** Container of the final key; undefined when the path is empty. */
|
|
6
|
+
parent: unknown;
|
|
7
|
+
key: string | number;
|
|
8
|
+
leaf: unknown;
|
|
9
|
+
};
|
|
10
|
+
declare const walkStatePath: (root: unknown, path: StatePath, mode: "read" | "parent" | "resolve") => WalkedPath | undefined;
|
|
11
|
+
declare const readAtPath: (root: unknown, path: StatePath) => unknown;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { StatePath, WalkedPath, isUnsafeKey, readAtPath, walkStatePath };
|
|
14
|
+
//# sourceMappingURL=statePath.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"statePath.d.ts","names":[],"sources":["../../src/util/statePath.ts"],"mappings":";KAAY;cAEC,cACX,sBACA;KAGU;;EAEV;EACA;EACA;;cAQW,gBACX,eACA,MAAM,WACN,wCACC;cAoCU,aAAc,eAAe,MAAM"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//#region src/util/statePath.ts
|
|
2
|
+
const isUnsafeKey = (key, parent) => key === "__proto__" || key === "prototype" && parent === "constructor";
|
|
3
|
+
const walkStatePath = (root, path, mode) => {
|
|
4
|
+
let parent = void 0;
|
|
5
|
+
let key = "";
|
|
6
|
+
let node = root;
|
|
7
|
+
for (let depth = 0; depth < path.length; depth++) {
|
|
8
|
+
const k = path[depth];
|
|
9
|
+
const mustResolve = mode === "resolve" || mode === "parent" && depth < path.length - 1;
|
|
10
|
+
if (mode !== "read") if (typeof k === "number") {
|
|
11
|
+
if (!Array.isArray(node) || !Number.isInteger(k) || k < 0) return;
|
|
12
|
+
if (mustResolve && k >= node.length) return void 0;
|
|
13
|
+
} else {
|
|
14
|
+
if (isUnsafeKey(k, path[depth - 1]) || typeof node !== "object" || node === null || Array.isArray(node)) return;
|
|
15
|
+
if (mustResolve && !Object.hasOwn(node, k)) return void 0;
|
|
16
|
+
}
|
|
17
|
+
parent = node;
|
|
18
|
+
key = k;
|
|
19
|
+
node = typeof node === "object" && node !== null && Object.hasOwn(node, k) ? node[k] : void 0;
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
parent,
|
|
23
|
+
key,
|
|
24
|
+
leaf: node
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
const readAtPath = (root, path) => walkStatePath(root, path, "read").leaf;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { isUnsafeKey, readAtPath, walkStatePath };
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=statePath.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"statePath.js","names":["StatePath","isUnsafeKey","key","parent","WalkedPath","leaf","walkStatePath","root","path","mode","undefined","node","depth","length","k","mustResolve","Array","isArray","Number","isInteger","Object","hasOwn","Record","readAtPath"],"sources":["../../src/util/statePath.ts"],"sourcesContent":["export type StatePath = readonly (string | number)[];\n\nexport const isUnsafeKey = (\n key: string | number,\n parent: string | number | undefined,\n) => key === \"__proto__\" || (key === \"prototype\" && parent === \"constructor\");\n\nexport type WalkedPath = {\n /** Container of the final key; undefined when the path is empty. */\n parent: unknown;\n key: string | number;\n leaf: unknown;\n};\n\n// \"read\": lenient, never fails — missing steps yield an undefined leaf.\n// \"parent\": every step before the last must resolve; the leaf may not exist.\n// \"resolve\": every step including the last must resolve.\n// Strict modes require array+integer for number keys, plain object for string\n// keys, and reject unsafe (prototype-polluting) segments.\nexport const walkStatePath = (\n root: unknown,\n path: StatePath,\n mode: \"read\" | \"parent\" | \"resolve\",\n): WalkedPath | undefined => {\n let parent: unknown = undefined;\n let key: string | number = \"\";\n let node: unknown = root;\n for (let depth = 0; depth < path.length; depth++) {\n const k = path[depth]!;\n const mustResolve =\n mode === \"resolve\" || (mode === \"parent\" && depth < path.length - 1);\n if (mode !== \"read\") {\n if (typeof k === \"number\") {\n if (!Array.isArray(node) || !Number.isInteger(k) || k < 0) {\n return undefined;\n }\n if (mustResolve && k >= node.length) return undefined;\n } else {\n if (\n isUnsafeKey(k, path[depth - 1]) ||\n typeof node !== \"object\" ||\n node === null ||\n Array.isArray(node)\n ) {\n return undefined;\n }\n if (mustResolve && !Object.hasOwn(node, k)) return undefined;\n }\n }\n parent = node;\n key = k;\n node =\n typeof node === \"object\" && node !== null && Object.hasOwn(node, k)\n ? (node as Record<string | number, unknown>)[k]\n : undefined;\n }\n return { parent, key, leaf: node };\n};\n\nexport const readAtPath = (root: unknown, path: StatePath): unknown =>\n walkStatePath(root, path, \"read\")!.leaf;\n"],"mappings":";AAEA,MAAaC,eACXC,KACAC,WACGD,QAAQ,eAAgBA,QAAQ,eAAeC,WAAW;AAc/D,MAAaG,iBACXC,MACAC,MACAC,SAC2B;CAC3B,IAAIN,SAAkBO,KAAAA;CACtB,IAAIR,MAAuB;CAC3B,IAAIS,OAAgBJ;CACpB,KAAK,IAAIK,QAAQ,GAAGA,QAAQJ,KAAKK,QAAQD,SAAS;EAChD,MAAME,IAAIN,KAAKI;EACf,MAAMG,cACJN,SAAS,aAAcA,SAAS,YAAYG,QAAQJ,KAAKK,SAAS;EACpE,IAAIJ,SAAS,QACX,IAAI,OAAOK,MAAM,UAAU;GACzB,IAAI,CAACE,MAAMC,QAAQN,IAAI,KAAK,CAACO,OAAOC,UAAUL,CAAC,KAAKA,IAAI,GACtD;GAEF,IAAIC,eAAeD,KAAKH,KAAKE,QAAQ,OAAOH,KAAAA;EAC9C,OAAO;GACL,IACET,YAAYa,GAAGN,KAAKI,QAAQ,EAAE,KAC9B,OAAOD,SAAS,YAChBA,SAAS,QACTK,MAAMC,QAAQN,IAAI,GAElB;GAEF,IAAII,eAAe,CAACK,OAAOC,OAAOV,MAAMG,CAAC,GAAG,OAAOJ,KAAAA;EACrD;EAEFP,SAASQ;EACTT,MAAMY;EACNH,OACE,OAAOA,SAAS,YAAYA,SAAS,QAAQS,OAAOC,OAAOV,MAAMG,CAAC,IAC7DH,KAA0CG,KAC3CJ,KAAAA;CACR;CACA,OAAO;EAAEP;EAAQD;EAAKG,MAAMM;CAAK;AACnC;AAEA,MAAaY,cAAchB,MAAeC,SACxCF,cAAcC,MAAMC,MAAM,MAAM,CAAC,CAAEH"}
|
package/dist/wire/meta.d.ts
CHANGED
|
@@ -17,6 +17,11 @@ declare const encodeParams: (params: readonly unknown[]) => {
|
|
|
17
17
|
params: unknown[];
|
|
18
18
|
meta?: StatewireMetaEntry[];
|
|
19
19
|
};
|
|
20
|
+
/** Encode Date instances inside a wire value, recording their paths in meta. */
|
|
21
|
+
declare const encodeValue: (value: unknown) => {
|
|
22
|
+
value: unknown;
|
|
23
|
+
meta?: StatewireMetaEntry[];
|
|
24
|
+
};
|
|
20
25
|
//#endregion
|
|
21
|
-
export { StatewireMetaEntry, encodeParams, reviveOps, reviveResponse, reviveValue };
|
|
26
|
+
export { StatewireMetaEntry, encodeParams, encodeValue, reviveOps, reviveResponse, reviveValue };
|
|
22
27
|
//# sourceMappingURL=meta.d.ts.map
|
package/dist/wire/meta.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"meta.d.ts","names":[],"sources":["../../src/wire/meta.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"meta.d.ts","names":[],"sources":["../../src/wire/meta.ts"],"mappings":";KAEY;EACV;EACA;;;;cAiBW,cAAe,gBAAgB;cAwB/B,YAAa;cAOb,iBAAkB;EAAY;EAAmB;GAC5D,UAAU,MACT;;;cA+BU,eACX;EACG;EAAmB,OAAO;;;cAOlB,cACX;EACG;EAAgB,OAAO"}
|