statewire 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/transports/testing.d.ts +1 -2
- package/dist/transports/testing.d.ts.map +1 -1
- package/dist/transports/testing.js +1 -3
- package/dist/transports/testing.js.map +1 -1
- package/dist/util/recordMutations.d.ts +3 -1
- package/dist/util/recordMutations.d.ts.map +1 -1
- package/dist/util/recordMutations.js +3 -1
- package/dist/util/recordMutations.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/transports/testing.ts +1 -6
- package/src/util/recordMutations.ts +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -2,4 +2,5 @@ import { StatewireClient, StatewireResource, useStatewire } from "./statewire.js
|
|
|
2
2
|
import { Statewire, StatewireSendError } from "./types.js";
|
|
3
3
|
import { StatewireSSE } from "./transports/sse.js";
|
|
4
4
|
import { StatewireWS } from "./transports/ws.js";
|
|
5
|
-
|
|
5
|
+
import { applyOptimistic } from "./util/recordMutations.js";
|
|
6
|
+
export { type Statewire, StatewireClient, StatewireResource, StatewireSSE, StatewireSendError, StatewireWS, applyOptimistic, useStatewire };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { applyOptimistic } from "./util/recordMutations.js";
|
|
1
2
|
import { StatewireSendError } from "./types.js";
|
|
2
3
|
import { StatewireClient, StatewireResource, useStatewire } from "./statewire.js";
|
|
3
4
|
import { StatewireSSE } from "./transports/sse.js";
|
|
4
5
|
import { StatewireWS } from "./transports/ws.js";
|
|
5
|
-
export { StatewireClient, StatewireResource, StatewireSSE, StatewireSendError, StatewireWS, useStatewire };
|
|
6
|
+
export { StatewireClient, StatewireResource, StatewireSSE, StatewireSendError, StatewireWS, applyOptimistic, useStatewire };
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { Statewire } from "../types.js";
|
|
2
2
|
import { StatewireEnvelope } from "../client/connection.js";
|
|
3
|
+
import { applyOptimistic } from "../util/recordMutations.js";
|
|
3
4
|
import { SocketRecorder, commandFrame, createCounterElement, createSocketRecorder, settle } from "./fixtures.js";
|
|
4
5
|
import { ResourceElement } from "@assistant-ui/tap";
|
|
5
6
|
//#region src/transports/testing.d.ts
|
|
6
7
|
/** Let queued microtasks and op batches drain through the statewire client. */
|
|
7
8
|
declare const tick: () => Promise<void>;
|
|
8
|
-
/** Run a mutator against a recording draft, as the client does for `optimistic`. */
|
|
9
|
-
declare const applyOptimistic: <TState>(state: TState, mutate: (draft: TState) => void) => TState;
|
|
10
9
|
type MockTransport<TState> = {
|
|
11
10
|
/** Pass as the `transport:` option of useStatewire / Harness. */
|
|
12
11
|
transport: ResourceElement<Statewire<TState | undefined, any, any>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.d.ts","names":[],"sources":["../../src/transports/testing.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"testing.d.ts","names":[],"sources":["../../src/transports/testing.ts"],"mappings":";;;;;;;cAUa,YAAI;KAIL,cAAc;;EAExB,WAAW,gBAAgB,UAAU;;EAErC;IAAY;IAAiB;IAAoB;;;EAEjD,WAAW,OAAO,WAAW;;EAE7B,MAAM,gBAAgB;;EAEtB,eAAe,UAAU,sBAAsB;;EAE/C,SAAS,gBAAgB,qBAAqB;WACrC;WACA;;cAUE,sBAAuB,QAClC,cAAc,WACb,cAAc"}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { applyOptimistic } from "../util/recordMutations.js";
|
|
2
2
|
import { StatewireSSE } from "./sse.js";
|
|
3
3
|
import { openSSEStream, sseFrame } from "./fake-sse.js";
|
|
4
4
|
import { commandFrame, createCounterElement, createSocketRecorder, settle } from "./fixtures.js";
|
|
5
5
|
//#region src/transports/testing.ts
|
|
6
6
|
/** Let queued microtasks and op batches drain through the statewire client. */
|
|
7
7
|
const tick = () => new Promise((r) => setTimeout(r, 0));
|
|
8
|
-
/** Run a mutator against a recording draft, as the client does for `optimistic`. */
|
|
9
|
-
const applyOptimistic = (state, mutate) => recordMutations(state, mutate).state;
|
|
10
8
|
/**
|
|
11
9
|
* A mock statewire backend: serves `initialState` as the attach snapshot,
|
|
12
10
|
* records command POSTs (receipt only — acks and results are delivered from
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.js","names":["ResourceElement","StatewireEnvelope","
|
|
1
|
+
{"version":3,"file":"testing.js","names":["ResourceElement","StatewireEnvelope","StatewireSSE","Statewire","openSSEStream","sseFrame","FakeStreamHandle","tick","Promise","r","setTimeout","applyOptimistic","MockTransport","transport","TState","commands","method","params","context","setState","state","ack","seq","sendEnvelope","envelope","finish","reason","message","clientId","attached","mockCount","createMockTransport","initialState","finished","clients","Map","streams","Set","stream","send","fakeFetch","input","RequestInfo","URL","init","RequestInit","Response","TypeError","url","String","headers","Headers","id","get","endsWith","lastSeq","set","handle","ops","op","path","value","syn","lease","signal","undefined","Number","Math","max","push","JSON","parse","body","json","status","fetch","next","fin","close","size"],"sources":["../../src/transports/testing.ts"],"sourcesContent":["import type { ResourceElement } from \"@assistant-ui/tap\";\nimport type { StatewireEnvelope } from \"../client/connection\";\nimport { StatewireSSE } from \"./sse\";\nimport type { Statewire } from \"../types\";\nimport { openSSEStream, sseFrame } from \"./fake-sse\";\nimport type { FakeStreamHandle } from \"./fake-sse\";\n\nexport * from \"./fixtures\";\n\n/** Let queued microtasks and op batches drain through the statewire client. */\nexport const tick = () => new Promise<void>((r) => setTimeout(r, 0));\n\nexport { applyOptimistic } from \"../util/recordMutations\";\n\nexport type MockTransport<TState> = {\n /** Pass as the `transport:` option of useStatewire / Harness. */\n transport: ResourceElement<Statewire<TState | undefined, any, any>>;\n /** Captured `/commands` POST bodies, in POST order. */\n commands: { method?: string; params?: unknown[]; context?: unknown }[];\n /** Replace the replicated state and broadcast it as a root snapshot op. */\n setState: (state: TState) => Promise<void>;\n /** Deliver an ack watermark on the stream. */\n ack: (seq: number) => Promise<void>;\n /** Deliver an arbitrary envelope on the stream. */\n sendEnvelope: (envelope: StatewireEnvelope) => Promise<void>;\n /** End the stream with a fin; further attaches fail like a dead server. */\n finish: (reason: string, message?: string) => Promise<void>;\n readonly clientId: string;\n readonly attached: boolean;\n};\n\n/**\n * A mock statewire backend: serves `initialState` as the attach snapshot,\n * records command POSTs (receipt only — acks and results are delivered from\n * test code), and exposes the wire as a transport ResourceElement.\n */\nlet mockCount = 0;\n\nexport const createMockTransport = <TState>(\n initialState: TState,\n): MockTransport<TState> => {\n let state: unknown = initialState;\n let clientId = \"\";\n let finished = false;\n const clients = new Map<string, number>();\n const streams = new Set<FakeStreamHandle>();\n const commands: MockTransport<unknown>[\"commands\"] = [];\n\n const sendEnvelope = (envelope: StatewireEnvelope) => {\n for (const stream of streams) stream.send(sseFrame(envelope));\n return tick();\n };\n\n const fakeFetch = (async (\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> => {\n if (finished) throw new TypeError(\"fetch failed (finished)\");\n const url = String(input);\n const headers = new Headers(init?.headers);\n const id = headers.get(\"Statewire-Client-Id\") ?? \"\";\n if (url.endsWith(\"/stream\")) {\n clientId = id;\n const lastSeq = clients.get(id) ?? 0;\n clients.set(id, lastSeq);\n return openSSEStream(\n streams,\n id,\n (handle) => {\n handle.send(\n sseFrame({\n ops: [{ op: \"replace\", path: [], value: state }],\n syn: { lastSeq, lease: \"mock-lease\" },\n }),\n );\n },\n init?.signal ?? undefined,\n );\n }\n if (url.endsWith(\"/commands\")) {\n const seq = Number(headers.get(\"Statewire-Command-Seq\"));\n clients.set(id, Math.max(clients.get(id) ?? 0, seq));\n commands.push(\n JSON.parse(\n String(init?.body),\n ) as MockTransport<unknown>[\"commands\"][number],\n );\n return Response.json({});\n }\n return new Response(null, { status: 404 });\n }) as typeof fetch;\n\n return {\n transport: StatewireSSE({\n url: `http://mock-${++mockCount}/api`,\n fetch: fakeFetch,\n }),\n commands,\n setState: (next) => {\n state = next;\n return sendEnvelope({ ops: [{ op: \"replace\", path: [], value: next }] });\n },\n ack: (seq) => sendEnvelope({ ack: seq }),\n sendEnvelope,\n finish: async (reason, message) => {\n await sendEnvelope({\n fin: { reason, ...(message !== undefined && { message }) },\n });\n finished = true;\n for (const stream of [...streams]) stream.close();\n await tick();\n },\n get clientId() {\n return clientId;\n },\n get attached() {\n return streams.size > 0;\n },\n };\n};\n"],"mappings":";;;;;;AAUA,MAAaO,aAAa,IAAIC,SAAeC,MAAMC,WAAWD,GAAG,CAAC,CAAC;;;;;;AA0BnE,IAAIqB,YAAY;AAEhB,MAAaC,uBACXC,iBAC0B;CAC1B,IAAIZ,QAAiBY;CACrB,IAAIJ,WAAW;CACf,IAAIK,WAAW;CACf,MAAMC,0BAAU,IAAIC,IAAoB;CACxC,MAAMC,0BAAU,IAAIC,IAAsB;CAC1C,MAAMtB,WAA+C,CAAA;CAErD,MAAMQ,gBAAgBC,aAAgC;EACpD,KAAK,MAAMc,UAAUF,SAASE,OAAOC,KAAKlC,SAASmB,QAAQ,CAAC;EAC5D,OAAOjB,KAAK;CACd;CAEA,MAAMiC,aAAa,OACjBC,OACAG,SACsB;EACtB,IAAIX,UAAU,MAAM,IAAIc,UAAU,yBAAyB;EAC3D,MAAMC,MAAMC,OAAOR,KAAK;EACxB,MAAMS,UAAU,IAAIC,QAAQP,MAAMM,OAAO;EACzC,MAAME,KAAKF,QAAQG,IAAI,qBAAqB,KAAK;EACjD,IAAIL,IAAIM,SAAS,SAAS,GAAG;GAC3B1B,WAAWwB;GACX,MAAMG,UAAUrB,QAAQmB,IAAID,EAAE,KAAK;GACnClB,QAAQsB,IAAIJ,IAAIG,OAAO;GACvB,OAAOnD,cACLgC,SACAgB,KACCK,WAAW;IACVA,OAAOlB,KACLlC,SAAS;KACPqD,KAAK,CAAC;MAAEC,IAAI;MAAWC,MAAM,CAAA;MAAIC,OAAOzC;KAAM,CAAC;KAC/C0C,KAAK;MAAEP;MAASQ,OAAO;KAAa;IACtC,CAAC,CACH;GACF,GACAnB,MAAMoB,UAAUC,KAAAA,CAClB;EACF;EACA,IAAIjB,IAAIM,SAAS,WAAW,GAAG;GAC7B,MAAMhC,MAAM4C,OAAOhB,QAAQG,IAAI,uBAAuB,CAAC;GACvDnB,QAAQsB,IAAIJ,IAAIe,KAAKC,IAAIlC,QAAQmB,IAAID,EAAE,KAAK,GAAG9B,GAAG,CAAC;GACnDP,SAASsD,KACPC,KAAKC,MACHtB,OAAOL,MAAM4B,IAAI,CACnB,CACF;GACA,OAAO1B,SAAS2B,KAAK,CAAC,CAAC;EACzB;EACA,OAAO,IAAI3B,SAAS,MAAM,EAAE4B,QAAQ,IAAI,CAAC;CAC3C;CAEA,OAAO;EACL7D,WAAWX,aAAa;GACtB8C,KAAK,eAAe,EAAElB,UAAS;GAC/B6C,OAAOnC;EACT,CAAC;EACDzB;EACAI,WAAWyD,SAAS;GAClBxD,QAAQwD;GACR,OAAOrD,aAAa,EAAEmC,KAAK,CAAC;IAAEC,IAAI;IAAWC,MAAM,CAAA;IAAIC,OAAOe;GAAK,CAAC,EAAE,CAAC;EACzE;EACAvD,MAAMC,QAAQC,aAAa,EAAEF,KAAKC,IAAI,CAAC;EACvCC;EACAE,QAAQ,OAAOC,QAAQC,YAAY;GACjC,MAAMJ,aAAa,EACjBsD,KAAK;IAAEnD;IAAQ,GAAIC,YAAYsC,KAAAA,KAAa,EAAEtC,QAAQ;GAAG,EAC3D,CAAC;GACDM,WAAW;GACX,KAAK,MAAMK,UAAU,CAAC,GAAGF,OAAO,GAAGE,OAAOwC,MAAM;GAChD,MAAMvE,KAAK;EACb;EACA,IAAIqB,WAAW;GACb,OAAOA;EACT;EACA,IAAIC,WAAW;GACb,OAAOO,QAAQ2C,OAAO;EACxB;CACF;AACF"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
//#region src/util/recordMutations.d.ts
|
|
2
2
|
type PathKey = string | number;
|
|
3
3
|
type Path = readonly PathKey[];
|
|
4
|
+
/** Run a mutator against a recording draft, as the client does for `optimistic`. */
|
|
5
|
+
declare const applyOptimistic: <TState>(state: TState, mutate: (draft: TState) => void) => TState;
|
|
4
6
|
declare const recordMutations: <T>(base: T, mutate: (draft: T) => void) => {
|
|
5
7
|
state: T;
|
|
6
8
|
changedPaths: Path[];
|
|
7
9
|
};
|
|
8
10
|
//#endregion
|
|
9
|
-
export { recordMutations };
|
|
11
|
+
export { applyOptimistic, recordMutations };
|
|
10
12
|
//# sourceMappingURL=recordMutations.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recordMutations.d.ts","names":[],"sources":["../../src/util/recordMutations.ts"],"mappings":";KAEK;KACA,gBAAgB;
|
|
1
|
+
{"version":3,"file":"recordMutations.d.ts","names":[],"sources":["../../src/util/recordMutations.ts"],"mappings":";KAEK;KACA,gBAAgB;;cAwER,kBAAmB,QAC9B,OAAO,QACP,SAAS,OAAO,oBACf;cAEU,kBAAmB,GAC9B,MAAM,GACN,SAAS,OAAO;EACb,OAAO;EAAG,cAAc"}
|
|
@@ -34,6 +34,8 @@ const finalize = (node) => {
|
|
|
34
34
|
}
|
|
35
35
|
return result;
|
|
36
36
|
};
|
|
37
|
+
/** Run a mutator against a recording draft, as the client does for `optimistic`. */
|
|
38
|
+
const applyOptimistic = (state, mutate) => recordMutations(state, mutate).state;
|
|
37
39
|
const recordMutations = (base, mutate) => {
|
|
38
40
|
if (!isDraftable(base)) throw new Error("statewire: cannot record mutations on primitive state");
|
|
39
41
|
const changedPaths = [];
|
|
@@ -104,6 +106,6 @@ const recordMutations = (base, mutate) => {
|
|
|
104
106
|
};
|
|
105
107
|
};
|
|
106
108
|
//#endregion
|
|
107
|
-
export { recordMutations };
|
|
109
|
+
export { applyOptimistic, recordMutations };
|
|
108
110
|
|
|
109
111
|
//# sourceMappingURL=recordMutations.js.map
|
|
@@ -1 +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","
|
|
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","applyOptimistic","state","TState","mutate","draft","recordMutations","T","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\n/** Run a mutator against a recording draft, as the client does for `optimistic`. */\nexport const applyOptimistic = <TState>(\n state: TState,\n mutate: (draft: TState) => void,\n): TState => recordMutations(state, mutate).state;\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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"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -3,3 +3,4 @@ export { StatewireClient, useStatewire, StatewireResource } from "./statewire";
|
|
|
3
3
|
export { StatewireSSE } from "./transports/sse";
|
|
4
4
|
export { StatewireWS } from "./transports/ws";
|
|
5
5
|
export { StatewireSendError } from "./types";
|
|
6
|
+
export { applyOptimistic } from "./util/recordMutations";
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { ResourceElement } from "@assistant-ui/tap";
|
|
2
2
|
import type { StatewireEnvelope } from "../client/connection";
|
|
3
|
-
import { recordMutations } from "../util/recordMutations";
|
|
4
3
|
import { StatewireSSE } from "./sse";
|
|
5
4
|
import type { Statewire } from "../types";
|
|
6
5
|
import { openSSEStream, sseFrame } from "./fake-sse";
|
|
@@ -11,11 +10,7 @@ export * from "./fixtures";
|
|
|
11
10
|
/** Let queued microtasks and op batches drain through the statewire client. */
|
|
12
11
|
export const tick = () => new Promise<void>((r) => setTimeout(r, 0));
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
export const applyOptimistic = <TState>(
|
|
16
|
-
state: TState,
|
|
17
|
-
mutate: (draft: TState) => void,
|
|
18
|
-
): TState => recordMutations(state, mutate).state;
|
|
13
|
+
export { applyOptimistic } from "../util/recordMutations";
|
|
19
14
|
|
|
20
15
|
export type MockTransport<TState> = {
|
|
21
16
|
/** Pass as the `transport:` option of useStatewire / Harness. */
|
|
Binary file
|