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
package/dist/client/core.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { recordMutations } from "../util/recordMutations.js";
|
|
2
|
+
import { createMethodProxy } from "../util/methodProxy.js";
|
|
3
3
|
import { StatewireSendError } from "../types.js";
|
|
4
4
|
import { getCommandsHost, getEmitHost, getStateHost } from "../host/host.js";
|
|
5
|
+
import { createMacrotaskPoster } from "../util/macrotask.js";
|
|
6
|
+
import { createChanges } from "./changes.js";
|
|
7
|
+
import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
|
|
8
|
+
import { useEffect, useState } from "@assistant-ui/tap/react-shim";
|
|
5
9
|
//#region src/client/core.ts
|
|
6
10
|
const LIVE_CONNECTION = {
|
|
7
11
|
status: "live",
|
|
@@ -12,27 +16,7 @@ const failureOf = (error) => error instanceof StatewireSendError ? error.failure
|
|
|
12
16
|
cause: "server",
|
|
13
17
|
message: error instanceof Error ? error.message : String(error)
|
|
14
18
|
};
|
|
15
|
-
const
|
|
16
|
-
if (typeof MessageChannel !== "undefined") {
|
|
17
|
-
let port1 = null;
|
|
18
|
-
let port2;
|
|
19
|
-
return () => {
|
|
20
|
-
if (!port1) {
|
|
21
|
-
const channel = new MessageChannel();
|
|
22
|
-
channel.port1.onmessage = () => {
|
|
23
|
-
port1?.unref?.();
|
|
24
|
-
callback();
|
|
25
|
-
};
|
|
26
|
-
port1 = channel.port1;
|
|
27
|
-
port2 = channel.port2;
|
|
28
|
-
}
|
|
29
|
-
port1.ref?.();
|
|
30
|
-
port2.postMessage(null);
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
return () => setTimeout(callback, 0);
|
|
34
|
-
};
|
|
35
|
-
const createStatewireCore = (getOptions) => {
|
|
19
|
+
const createCore = (getOptions) => {
|
|
36
20
|
let value = null;
|
|
37
21
|
let source = null;
|
|
38
22
|
let overlays = [];
|
|
@@ -48,7 +32,6 @@ const createStatewireCore = (getOptions) => {
|
|
|
48
32
|
let stale = false;
|
|
49
33
|
let overlaysDirty = false;
|
|
50
34
|
let flushing = false;
|
|
51
|
-
let disposed = false;
|
|
52
35
|
const listeners = /* @__PURE__ */ new Set();
|
|
53
36
|
const post = createMacrotaskPoster(() => {
|
|
54
37
|
scheduled = false;
|
|
@@ -56,7 +39,7 @@ const createStatewireCore = (getOptions) => {
|
|
|
56
39
|
});
|
|
57
40
|
let scheduled = false;
|
|
58
41
|
const schedule = () => {
|
|
59
|
-
if (scheduled
|
|
42
|
+
if (scheduled) return;
|
|
60
43
|
scheduled = true;
|
|
61
44
|
post();
|
|
62
45
|
};
|
|
@@ -67,7 +50,7 @@ const createStatewireCore = (getOptions) => {
|
|
|
67
50
|
const drainCanonical = () => {
|
|
68
51
|
if (source) {
|
|
69
52
|
pendingChanged.push(...source.takeChanged());
|
|
70
|
-
canonical = source.
|
|
53
|
+
canonical = source.snapshot();
|
|
71
54
|
} else if (value && value.state !== canonical) {
|
|
72
55
|
pendingChanged.push([]);
|
|
73
56
|
canonical = value.state;
|
|
@@ -108,7 +91,7 @@ const createStatewireCore = (getOptions) => {
|
|
|
108
91
|
};
|
|
109
92
|
const getConnection = () => value?.connection ?? LIVE_CONNECTION;
|
|
110
93
|
const flush = () => {
|
|
111
|
-
if (
|
|
94
|
+
if (!value) return;
|
|
112
95
|
if (flushing) {
|
|
113
96
|
schedule();
|
|
114
97
|
return;
|
|
@@ -135,7 +118,7 @@ const createStatewireCore = (getOptions) => {
|
|
|
135
118
|
updates = [];
|
|
136
119
|
for (const update of batch) options.onCommandChange?.(update, { state: published });
|
|
137
120
|
}
|
|
138
|
-
if (events.length > 0) {
|
|
121
|
+
if (events.length > 0 && published !== void 0) {
|
|
139
122
|
const batch = events;
|
|
140
123
|
events = [];
|
|
141
124
|
for (const event of batch) options.onEvent?.(event, { state: published });
|
|
@@ -188,7 +171,7 @@ const createStatewireCore = (getOptions) => {
|
|
|
188
171
|
schedule();
|
|
189
172
|
};
|
|
190
173
|
const attach = (next) => {
|
|
191
|
-
if (
|
|
174
|
+
if (next === value) return;
|
|
192
175
|
const prev = value;
|
|
193
176
|
value = next;
|
|
194
177
|
const nextSource = next.source ?? (typeof next.state === "object" && next.state !== null ? getStateHost(next.state) : void 0) ?? null;
|
|
@@ -223,38 +206,23 @@ const createStatewireCore = (getOptions) => {
|
|
|
223
206
|
value = null;
|
|
224
207
|
}
|
|
225
208
|
};
|
|
226
|
-
const memo = /* @__PURE__ */ new Map();
|
|
227
209
|
return {
|
|
228
210
|
attach,
|
|
229
211
|
detach,
|
|
230
|
-
dispose: () => {
|
|
231
|
-
detach();
|
|
232
|
-
disposed = true;
|
|
233
|
-
},
|
|
234
212
|
getState: () => {
|
|
235
213
|
recompute();
|
|
236
214
|
return published;
|
|
237
215
|
},
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
const send = value.commands[method];
|
|
249
|
-
const promise = send(...params);
|
|
250
|
-
flush();
|
|
251
|
-
return promise;
|
|
252
|
-
};
|
|
253
|
-
memo.set(method, fn);
|
|
254
|
-
}
|
|
255
|
-
return fn;
|
|
256
|
-
} }),
|
|
257
|
-
flush,
|
|
216
|
+
commands: createMethodProxy((method, params) => {
|
|
217
|
+
if (!value) return Promise.reject(new StatewireSendError({
|
|
218
|
+
fate: "aborted",
|
|
219
|
+
cause: "unmount"
|
|
220
|
+
}));
|
|
221
|
+
const send = value.commands[method];
|
|
222
|
+
const promise = send(...params);
|
|
223
|
+
flush();
|
|
224
|
+
return promise;
|
|
225
|
+
}),
|
|
258
226
|
subscribe: (listener) => {
|
|
259
227
|
listeners.add(listener);
|
|
260
228
|
return () => {
|
|
@@ -263,7 +231,42 @@ const createStatewireCore = (getOptions) => {
|
|
|
263
231
|
}
|
|
264
232
|
};
|
|
265
233
|
};
|
|
234
|
+
const useStatewireCore = (getOptions, value) => {
|
|
235
|
+
const $ = c(8);
|
|
236
|
+
let t0;
|
|
237
|
+
if ($[0] !== getOptions) {
|
|
238
|
+
t0 = () => createCore(getOptions);
|
|
239
|
+
$[0] = getOptions;
|
|
240
|
+
$[1] = t0;
|
|
241
|
+
} else t0 = $[1];
|
|
242
|
+
const [core] = useState(t0);
|
|
243
|
+
core.attach(value);
|
|
244
|
+
let t1;
|
|
245
|
+
if ($[2] !== core || $[3] !== value) {
|
|
246
|
+
t1 = () => {
|
|
247
|
+
core.attach(value);
|
|
248
|
+
};
|
|
249
|
+
$[2] = core;
|
|
250
|
+
$[3] = value;
|
|
251
|
+
$[4] = t1;
|
|
252
|
+
} else t1 = $[4];
|
|
253
|
+
useEffect(t1);
|
|
254
|
+
let t2;
|
|
255
|
+
let t3;
|
|
256
|
+
if ($[5] !== core) {
|
|
257
|
+
t2 = () => () => core.detach();
|
|
258
|
+
t3 = [core];
|
|
259
|
+
$[5] = core;
|
|
260
|
+
$[6] = t2;
|
|
261
|
+
$[7] = t3;
|
|
262
|
+
} else {
|
|
263
|
+
t2 = $[6];
|
|
264
|
+
t3 = $[7];
|
|
265
|
+
}
|
|
266
|
+
useEffect(t2, t3);
|
|
267
|
+
return core;
|
|
268
|
+
};
|
|
266
269
|
//#endregion
|
|
267
|
-
export { LIVE_CONNECTION,
|
|
270
|
+
export { LIVE_CONNECTION, useStatewireCore };
|
|
268
271
|
|
|
269
272
|
//# sourceMappingURL=core.js.map
|
package/dist/client/core.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.js","names":["createChanges","getCommandsHost","getEmitHost","getStateHost","CommandsInvocation","CommandsListener","recordMutations","StatewirePath","StatewireSendError","SendFailure","StatewireSendFailure","StatewireStateSource","StatewireTransport","StatewireCommand","StatewireCommandsProxy","StatewireCommandUpdate","StatewireOptimisticContext","StatewireOptions","StatewireConnection","LIVE_CONNECTION","status","reconnect","failureOf","error","failure","fate","cause","message","Error","String","createMacrotaskPoster","callback","MessageChannel","port1","MessagePort","ref","unref","port2","channel","onmessage","postMessage","setTimeout","Overlay","inv","dropped","StatewireCore","ReturnType","createStatewireCore","getOptions","value","source","overlays","updates","events","pendingChanged","canonical","published","overlayPaths","notifiedPublished","notifiedOverlayPaths","notifiedConnection","stale","overlaysDirty","flushing","disposed","listeners","Set","post","scheduled","flush","schedule","markStale","drainCanonical","push","takeChanged","root","state","recompute","prevCanonical","optimistic","next","paths","undefined","overlay","ctx","ts","seq","clientId","recorded","draft","method","params","changedPaths","console","getConnection","connection","options","stateChanged","prev","changed","onStateChange","changes","length","batch","update","onCommandChange","event","onEvent","connectionChanged","listener","note","extra","result","commandsListener","onStart","onPosted","onAck","onCovered","some","o","filter","onSettle","outcome","type","payload","eventSink","attach","nextSource","setOnChange","commands","setListener","emit","setSink","detach","memo","Map","Promise","Proxy","get","_target","fn","reject","send","Record","args","promise","set","dispose","getState","subscribe","add","delete"],"sources":["../../src/client/core.ts"],"sourcesContent":["import { createChanges } from \"./changes\";\nimport { getCommandsHost, getEmitHost, getStateHost } from \"../host/host\";\nimport type { CommandsInvocation, CommandsListener } from \"../host/host\";\nimport { recordMutations } from \"./optimistic\";\nimport type { StatewirePath } from \"../wire/ops\";\nimport { StatewireSendError } from \"../types\";\nimport type {\n SendFailure as StatewireSendFailure,\n StatewireStateSource,\n StatewireTransport,\n StatewireCommand,\n StatewireCommandsProxy,\n StatewireCommandUpdate,\n StatewireOptimisticContext,\n StatewireOptions,\n} from \"../types\";\nimport type { StatewireConnection } from \"../transports/sse\";\n\nexport const LIVE_CONNECTION: StatewireConnection = {\n status: \"live\",\n reconnect: () => {},\n};\n\nconst failureOf = (error: unknown): StatewireSendFailure =>\n error instanceof StatewireSendError\n ? error.failure\n : {\n fate: \"rejected\",\n cause: \"server\",\n message: error instanceof Error ? error.message : String(error),\n };\n\n// 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.\nconst 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\ntype Overlay = { inv: CommandsInvocation; dropped: boolean };\n\nexport type StatewireCore = ReturnType<typeof createStatewireCore>;\n\nexport const createStatewireCore = (\n getOptions: () => StatewireOptions<any, any, any>,\n) => {\n let value: StatewireTransport<unknown> | null = null;\n let source: StatewireStateSource | null = null;\n let overlays: Overlay[] = [];\n let updates: StatewireCommandUpdate[] = [];\n let events: unknown[] = [];\n let pendingChanged: StatewirePath[] = [];\n let canonical: unknown;\n let published: unknown;\n let overlayPaths: readonly StatewirePath[] = [];\n let notifiedPublished: unknown;\n let notifiedOverlayPaths: readonly StatewirePath[] = [];\n let notifiedConnection: StatewireConnection | null = null;\n let stale = false;\n let overlaysDirty = false;\n let flushing = false;\n let disposed = false;\n const listeners = new Set<() => void>();\n\n const post = createMacrotaskPoster(() => {\n scheduled = false;\n flush();\n });\n let scheduled = false;\n const schedule = () => {\n if (scheduled || disposed) return;\n scheduled = true;\n post();\n };\n\n const markStale = () => {\n stale = true;\n schedule();\n };\n\n const drainCanonical = () => {\n if (source) {\n pendingChanged.push(...source.takeChanged());\n canonical = source.root();\n } else if (value && value.state !== canonical) {\n pendingChanged.push([]);\n canonical = value.state;\n }\n };\n\n const recompute = () => {\n if (!stale || !value) return;\n stale = false;\n const prevCanonical = canonical;\n drainCanonical();\n if (canonical === prevCanonical && !overlaysDirty) return;\n overlaysDirty = false;\n const { optimistic } = getOptions();\n let next = canonical;\n let paths: readonly StatewirePath[] = [];\n if (optimistic && canonical !== undefined) {\n for (const overlay of overlays) {\n if (overlay.dropped) continue;\n const { inv } = overlay;\n const ctx: StatewireOptimisticContext = {\n ts: inv.ts,\n seq: inv.seq,\n ...(inv.clientId !== undefined && { clientId: inv.clientId }),\n };\n try {\n const recorded = recordMutations(next, (draft) =>\n optimistic(\n draft as never,\n { method: inv.method, params: inv.params } as StatewireCommand,\n ctx,\n ),\n );\n next = recorded.state;\n paths = [...paths, ...recorded.changedPaths];\n } catch (error) {\n overlay.dropped = true;\n console.error(\n `statewire: optimistic reducer threw for \"${inv.method}\"; dropping its optimistic update`,\n error,\n );\n }\n }\n }\n published = next;\n overlayPaths = paths;\n };\n\n const getConnection = (): StatewireConnection =>\n value?.connection ?? LIVE_CONNECTION;\n\n const flush = () => {\n if (disposed || !value) return;\n if (flushing) {\n schedule();\n return;\n }\n flushing = true;\n try {\n recompute();\n const options = getOptions();\n const stateChanged = published !== notifiedPublished;\n if (stateChanged) {\n const prev = notifiedPublished;\n const changed = [\n ...pendingChanged,\n ...notifiedOverlayPaths,\n ...overlayPaths,\n ];\n pendingChanged = [];\n notifiedPublished = published;\n notifiedOverlayPaths = overlayPaths;\n options.onStateChange?.(published as never, {\n changes: createChanges(prev, published, changed),\n });\n }\n if (updates.length > 0 && published !== undefined) {\n const batch = updates;\n updates = [];\n for (const update of batch) {\n options.onCommandChange?.(update, { state: published as never });\n }\n }\n if (events.length > 0) {\n const batch = events;\n events = [];\n for (const event of batch) {\n options.onEvent?.(event, { state: published as never });\n }\n }\n const connection = getConnection();\n const connectionChanged = connection !== notifiedConnection;\n notifiedConnection = connection;\n if (stateChanged || connectionChanged) {\n for (const listener of [...listeners]) listener();\n }\n } finally {\n flushing = false;\n }\n };\n\n const note = (\n inv: CommandsInvocation,\n status: StatewireCommandUpdate[\"status\"],\n extra?: { result?: unknown; failure?: StatewireSendFailure },\n ) => {\n if (!getOptions().onCommandChange) return;\n updates.push({\n method: inv.method,\n params: inv.params as never[],\n seq: inv.seq,\n status,\n ...extra,\n });\n schedule();\n };\n\n const commandsListener: CommandsListener = {\n onStart: (inv) => {\n overlays.push({ inv, dropped: false });\n overlaysDirty = true;\n markStale();\n note(inv, \"queued\");\n },\n onPosted: (inv) => note(inv, \"posted\"),\n onAck: (inv) => note(inv, \"acked\"),\n onCovered: (inv) => {\n if (!overlays.some((o) => o.inv === inv)) return;\n overlays = overlays.filter((o) => o.inv !== inv);\n overlaysDirty = true;\n markStale();\n },\n onSettle: (inv, outcome) => {\n note(\n inv,\n \"settled\",\n outcome.type === \"accepted\"\n ? { result: outcome.payload }\n : { failure: failureOf(outcome.error) },\n );\n flush();\n },\n };\n\n const eventSink = (event: unknown) => {\n if (!getOptions().onEvent) return;\n events.push(event);\n schedule();\n };\n\n const attach = (next: StatewireTransport<unknown>) => {\n if (disposed || next === value) return;\n const prev = value;\n value = next;\n const nextSource =\n next.source ??\n (typeof next.state === \"object\" && next.state !== null\n ? getStateHost(next.state)\n : undefined) ??\n null;\n if (nextSource !== source) {\n source?.setOnChange(null);\n source = nextSource;\n source?.setOnChange(markStale);\n markStale();\n }\n if (next.commands !== prev?.commands) {\n if (prev) getCommandsHost(prev.commands)?.setListener(null);\n getCommandsHost(next.commands)?.setListener(commandsListener);\n if (overlays.length > 0) {\n overlays = [];\n overlaysDirty = true;\n }\n markStale();\n }\n if (next.emit !== prev?.emit) {\n if (prev) getEmitHost(prev.emit)?.setSink(null);\n getEmitHost(next.emit)?.setSink(eventSink);\n }\n if (!source) markStale();\n schedule();\n };\n\n const detach = () => {\n source?.setOnChange(null);\n source = null;\n if (value) {\n getCommandsHost(value.commands)?.setListener(null);\n getEmitHost(value.emit)?.setSink(null);\n value = null;\n }\n };\n\n const memo = new Map<string, (...params: unknown[]) => Promise<unknown>>();\n const commands = new Proxy({} as StatewireCommandsProxy<any>, {\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[]) => {\n if (!value) {\n return Promise.reject(\n new StatewireSendError({ fate: \"aborted\", cause: \"unmount\" }),\n );\n }\n const send = (\n value.commands as Record<\n string,\n (...args: unknown[]) => Promise<unknown>\n >\n )[method]!;\n const promise = send(...params);\n flush();\n return promise;\n };\n memo.set(method, fn);\n }\n return fn;\n },\n });\n\n return {\n attach,\n detach,\n dispose: () => {\n detach();\n disposed = true;\n },\n getState: () => {\n recompute();\n return published;\n },\n getConnection,\n commands,\n flush,\n subscribe: (listener: () => void) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"core.js","names":["useEffect","useState","createChanges","getCommandsHost","getEmitHost","getStateHost","CommandsInvocation","CommandsListener","recordMutations","createMacrotaskPoster","createMethodProxy","StatewirePath","StatewireSendError","Statewire","StatewireClient","LIVE_CONNECTION","Connection","status","reconnect","failureOf","error","SendFailure","failure","fate","cause","message","Error","String","Overlay","inv","dropped","createCore","getOptions","Options","value","source","StateSource","overlays","updates","CommandUpdate","events","pendingChanged","canonical","published","overlayPaths","notifiedPublished","notifiedOverlayPaths","notifiedConnection","stale","overlaysDirty","flushing","listeners","Set","post","scheduled","flush","schedule","markStale","drainCanonical","push","takeChanged","snapshot","state","recompute","prevCanonical","optimistic","next","paths","undefined","overlay","ctx","OptimisticContext","ts","seq","clientId","recorded","draft","method","params","Command","changedPaths","console","getConnection","connection","options","stateChanged","prev","changed","onStateChange","changes","length","batch","update","onCommandChange","event","onEvent","connectionChanged","listener","note","extra","result","commandsListener","onStart","onPosted","onAck","onCovered","some","o","filter","onSettle","outcome","type","payload","eventSink","attach","nextSource","setOnChange","commands","setListener","emit","setSink","detach","CommandsProxy","Promise","reject","send","Record","args","promise","getState","subscribe","add","delete","useStatewireCore","$","_c","t0","core","t1","t2","t3"],"sources":["../../src/client/core.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { createChanges } from \"./changes\";\nimport { getCommandsHost, getEmitHost, getStateHost } from \"../host/host\";\nimport type { CommandsInvocation, CommandsListener } from \"../host/host\";\nimport { recordMutations } from \"../util/recordMutations\";\nimport { createMacrotaskPoster } from \"../util/macrotask\";\nimport { createMethodProxy } from \"../util/methodProxy\";\nimport type { StatewirePath } from \"../wire/ops\";\nimport { StatewireSendError } from \"../types\";\nimport type { Statewire } from \"../types\";\nimport type { StatewireClient } from \"../statewire\";\n\nexport const LIVE_CONNECTION: StatewireClient.Connection = {\n status: \"live\",\n reconnect: () => {},\n};\n\nconst failureOf = (error: unknown): StatewireClient.SendFailure =>\n error instanceof StatewireSendError\n ? error.failure\n : {\n fate: \"rejected\",\n cause: \"server\",\n message: error instanceof Error ? error.message : String(error),\n };\n\ntype Overlay = { inv: CommandsInvocation; dropped: boolean };\n\nconst createCore = (\n getOptions: () => StatewireClient.Options<any, any, any>,\n) => {\n let value: Statewire<unknown> | null = null;\n let source: Statewire.StateSource | null = null;\n let overlays: Overlay[] = [];\n let updates: Statewire.CommandUpdate[] = [];\n let events: unknown[] = [];\n let pendingChanged: StatewirePath[] = [];\n let canonical: unknown;\n let published: unknown;\n let overlayPaths: readonly StatewirePath[] = [];\n let notifiedPublished: unknown;\n let notifiedOverlayPaths: readonly StatewirePath[] = [];\n let notifiedConnection: StatewireClient.Connection | null = null;\n let stale = false;\n let overlaysDirty = false;\n let flushing = false;\n const listeners = new Set<() => void>();\n\n const post = createMacrotaskPoster(() => {\n scheduled = false;\n flush();\n });\n let scheduled = false;\n const schedule = () => {\n if (scheduled) return;\n scheduled = true;\n post();\n };\n\n const markStale = () => {\n stale = true;\n schedule();\n };\n\n const drainCanonical = () => {\n if (source) {\n pendingChanged.push(...source.takeChanged());\n canonical = source.snapshot();\n } else if (value && value.state !== canonical) {\n pendingChanged.push([]);\n canonical = value.state;\n }\n };\n\n const recompute = () => {\n if (!stale || !value) return;\n stale = false;\n const prevCanonical = canonical;\n drainCanonical();\n if (canonical === prevCanonical && !overlaysDirty) return;\n overlaysDirty = false;\n const { optimistic } = getOptions();\n let next = canonical;\n let paths: readonly StatewirePath[] = [];\n if (optimistic && canonical !== undefined) {\n for (const overlay of overlays) {\n if (overlay.dropped) continue;\n const { inv } = overlay;\n const ctx: StatewireClient.OptimisticContext = {\n ts: inv.ts,\n seq: inv.seq,\n ...(inv.clientId !== undefined && { clientId: inv.clientId }),\n };\n try {\n const recorded = recordMutations(next, (draft) =>\n optimistic(\n draft as never,\n { method: inv.method, params: inv.params } as Statewire.Command,\n ctx,\n ),\n );\n next = recorded.state;\n paths = [...paths, ...recorded.changedPaths];\n } catch (error) {\n overlay.dropped = true;\n console.error(\n `statewire: optimistic reducer threw for \"${inv.method}\"; dropping its optimistic update`,\n error,\n );\n }\n }\n }\n published = next;\n overlayPaths = paths;\n };\n\n const getConnection = (): StatewireClient.Connection =>\n value?.connection ?? LIVE_CONNECTION;\n\n const flush = () => {\n if (!value) return;\n if (flushing) {\n schedule();\n return;\n }\n flushing = true;\n try {\n recompute();\n const options = getOptions();\n const stateChanged = published !== notifiedPublished;\n if (stateChanged) {\n const prev = notifiedPublished;\n const changed = [\n ...pendingChanged,\n ...notifiedOverlayPaths,\n ...overlayPaths,\n ];\n pendingChanged = [];\n notifiedPublished = published;\n notifiedOverlayPaths = overlayPaths;\n options.onStateChange?.(published as never, {\n changes: createChanges(prev, published, changed),\n });\n }\n if (updates.length > 0 && published !== undefined) {\n const batch = updates;\n updates = [];\n for (const update of batch) {\n options.onCommandChange?.(update, { state: published as never });\n }\n }\n if (events.length > 0 && published !== undefined) {\n const batch = events;\n events = [];\n for (const event of batch) {\n options.onEvent?.(event, { state: published as never });\n }\n }\n const connection = getConnection();\n const connectionChanged = connection !== notifiedConnection;\n notifiedConnection = connection;\n if (stateChanged || connectionChanged) {\n for (const listener of [...listeners]) listener();\n }\n } finally {\n flushing = false;\n }\n };\n\n const note = (\n inv: CommandsInvocation,\n status: Statewire.CommandUpdate[\"status\"],\n extra?: { result?: unknown; failure?: StatewireClient.SendFailure },\n ) => {\n if (!getOptions().onCommandChange) return;\n updates.push({\n method: inv.method,\n params: inv.params as never[],\n seq: inv.seq,\n status,\n ...extra,\n });\n schedule();\n };\n\n const commandsListener: CommandsListener = {\n onStart: (inv) => {\n overlays.push({ inv, dropped: false });\n overlaysDirty = true;\n markStale();\n note(inv, \"queued\");\n },\n onPosted: (inv) => note(inv, \"posted\"),\n onAck: (inv) => note(inv, \"acked\"),\n onCovered: (inv) => {\n if (!overlays.some((o) => o.inv === inv)) return;\n overlays = overlays.filter((o) => o.inv !== inv);\n overlaysDirty = true;\n markStale();\n },\n onSettle: (inv, outcome) => {\n note(\n inv,\n \"settled\",\n outcome.type === \"accepted\"\n ? { result: outcome.payload }\n : { failure: failureOf(outcome.error) },\n );\n flush();\n },\n };\n\n const eventSink = (event: unknown) => {\n if (!getOptions().onEvent) return;\n events.push(event);\n schedule();\n };\n\n const attach = (next: Statewire<unknown>) => {\n if (next === value) return;\n const prev = value;\n value = next;\n const nextSource =\n next.source ??\n (typeof next.state === \"object\" && next.state !== null\n ? getStateHost(next.state)\n : undefined) ??\n null;\n if (nextSource !== source) {\n source?.setOnChange(null);\n source = nextSource;\n source?.setOnChange(markStale);\n markStale();\n }\n if (next.commands !== prev?.commands) {\n if (prev) getCommandsHost(prev.commands)?.setListener(null);\n getCommandsHost(next.commands)?.setListener(commandsListener);\n if (overlays.length > 0) {\n overlays = [];\n overlaysDirty = true;\n }\n markStale();\n }\n if (next.emit !== prev?.emit) {\n if (prev) getEmitHost(prev.emit)?.setSink(null);\n getEmitHost(next.emit)?.setSink(eventSink);\n }\n if (!source) markStale();\n schedule();\n };\n\n const detach = () => {\n source?.setOnChange(null);\n source = null;\n if (value) {\n getCommandsHost(value.commands)?.setListener(null);\n getEmitHost(value.emit)?.setSink(null);\n value = null;\n }\n };\n\n const commands = createMethodProxy<Statewire.CommandsProxy<any>>(\n (method, params) => {\n if (!value) {\n return Promise.reject(\n new StatewireSendError({ fate: \"aborted\", cause: \"unmount\" }),\n );\n }\n const send = (\n value.commands as Record<\n string,\n (...args: unknown[]) => Promise<unknown>\n >\n )[method]!;\n const promise = send(...params);\n flush();\n return promise;\n },\n );\n\n return {\n attach,\n detach,\n getState: () => {\n recompute();\n return published;\n },\n commands,\n subscribe: (listener: () => void) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n};\n\nexport const useStatewireCore = (\n getOptions: () => StatewireClient.Options<any, any, any>,\n value: Statewire<unknown>,\n) => {\n const [core] = useState(() => createCore(getOptions));\n // Attach during render so the first snapshot reads the transport's state;\n // re-attaching the same transport is a no-op and the single-slot host\n // listeners make a swap land on the last writer.\n core.attach(value);\n useEffect(() => {\n core.attach(value);\n });\n useEffect(() => () => core.detach(), [core]);\n return core;\n};\n"],"mappings":";;;;;;;;;AAYA,MAAae,kBAA8C;CACzDE,QAAQ;CACRC,iBAAiB,CAAC;AACpB;AAEA,MAAMC,aAAaC,UACjBA,iBAAiBR,qBACbQ,MAAME,UACN;CACEC,MAAM;CACNC,OAAO;CACPC,SAASL,iBAAiBM,QAAQN,MAAMK,UAAUE,OAAOP,KAAK;AAChE;AAIN,MAAMW,cACJC,eACG;CACH,IAAIE,QAAmC;CACvC,IAAIC,SAAuC;CAC3C,IAAIE,WAAsB,CAAA;CAC1B,IAAIC,UAAqC,CAAA;CACzC,IAAIE,SAAoB,CAAA;CACxB,IAAIC,iBAAkC,CAAA;CACtC,IAAIC;CACJ,IAAIC;CACJ,IAAIC,eAAyC,CAAA;CAC7C,IAAIC;CACJ,IAAIC,uBAAiD,CAAA;CACrD,IAAIC,qBAAwD;CAC5D,IAAIC,QAAQ;CACZ,IAAIC,gBAAgB;CACpB,IAAIC,WAAW;CACf,MAAMC,4BAAY,IAAIC,IAAgB;CAEtC,MAAMC,OAAO5C,4BAA4B;EACvC6C,YAAY;EACZC,MAAM;CACR,CAAC;CACD,IAAID,YAAY;CAChB,MAAME,iBAAiB;EACrB,IAAIF,WAAW;EACfA,YAAY;EACZD,KAAK;CACP;CAEA,MAAMI,kBAAkB;EACtBT,QAAQ;EACRQ,SAAS;CACX;CAEA,MAAME,uBAAuB;EAC3B,IAAIvB,QAAQ;GACVM,eAAekB,KAAK,GAAGxB,OAAOyB,YAAY,CAAC;GAC3ClB,YAAYP,OAAO0B,SAAS;EAC9B,OAAO,IAAI3B,SAASA,MAAM4B,UAAUpB,WAAW;GAC7CD,eAAekB,KAAK,CAAA,CAAE;GACtBjB,YAAYR,MAAM4B;EACpB;CACF;CAEA,MAAMC,kBAAkB;EACtB,IAAI,CAACf,SAAS,CAACd,OAAO;EACtBc,QAAQ;EACR,MAAMgB,gBAAgBtB;EACtBgB,eAAe;EACf,IAAIhB,cAAcsB,iBAAiB,CAACf,eAAe;EACnDA,gBAAgB;EAChB,MAAM,EAAEgB,eAAejC,WAAW;EAClC,IAAIkC,OAAOxB;EACX,IAAIyB,QAAkC,CAAA;EACtC,IAAIF,cAAcvB,cAAc0B,KAAAA,GAC9B,KAAK,MAAMC,WAAWhC,UAAU;GAC9B,IAAIgC,QAAQvC,SAAS;GACrB,MAAM,EAAED,QAAQwC;GAChB,MAAMC,MAAyC;IAC7CE,IAAI3C,IAAI2C;IACRC,KAAK5C,IAAI4C;IACT,GAAI5C,IAAI6C,aAAaN,KAAAA,KAAa,EAAEM,UAAU7C,IAAI6C,SAAS;GAC7D;GACA,IAAI;IACF,MAAMC,WAAWnE,gBAAgB0D,OAAOU,UACtCX,WACEW,OACA;KAAEC,QAAQhD,IAAIgD;KAAQC,QAAQjD,IAAIiD;IAAO,GACzCR,GACF,CACF;IACAJ,OAAOS,SAASb;IAChBK,QAAQ,CAAC,GAAGA,OAAO,GAAGQ,SAASK,YAAY;GAC7C,SAAS5D,OAAO;IACdiD,QAAQvC,UAAU;IAClBmD,QAAQ7D,MACN,4CAA4CS,IAAIgD,OAAM,oCACtDzD,KACF;GACF;EACF;EAEFuB,YAAYuB;EACZtB,eAAeuB;CACjB;CAEA,MAAMe,sBACJhD,OAAOiD,cAAcpE;CAEvB,MAAMwC,cAAc;EAClB,IAAI,CAACrB,OAAO;EACZ,IAAIgB,UAAU;GACZM,SAAS;GACT;EACF;EACAN,WAAW;EACX,IAAI;GACFa,UAAU;GACV,MAAMqB,UAAUpD,WAAW;GAC3B,MAAMqD,eAAe1C,cAAcE;GACnC,IAAIwC,cAAc;IAChB,MAAMC,OAAOzC;IACb,MAAM0C,UAAU;KACd,GAAG9C;KACH,GAAGK;KACH,GAAGF;IAAY;IAEjBH,iBAAiB,CAAA;IACjBI,oBAAoBF;IACpBG,uBAAuBF;IACvBwC,QAAQI,gBAAgB7C,WAAoB,EAC1C8C,SAASvF,cAAcoF,MAAM3C,WAAW4C,OAAO,EACjD,CAAC;GACH;GACA,IAAIjD,QAAQoD,SAAS,KAAK/C,cAAcyB,KAAAA,GAAW;IACjD,MAAMuB,QAAQrD;IACdA,UAAU,CAAA;IACV,KAAK,MAAMsD,UAAUD,OACnBP,QAAQS,kBAAkBD,QAAQ,EAAE9B,OAAOnB,UAAmB,CAAC;GAEnE;GACA,IAAIH,OAAOkD,SAAS,KAAK/C,cAAcyB,KAAAA,GAAW;IAChD,MAAMuB,QAAQnD;IACdA,SAAS,CAAA;IACT,KAAK,MAAMsD,SAASH,OAClBP,QAAQW,UAAUD,OAAO,EAAEhC,OAAOnB,UAAmB,CAAC;GAE1D;GACA,MAAMwC,aAAaD,cAAc;GACjC,MAAMc,oBAAoBb,eAAepC;GACzCA,qBAAqBoC;GACrB,IAAIE,gBAAgBW,mBAClB,KAAK,MAAMC,YAAY,CAAC,GAAG9C,SAAS,GAAG8C,SAAS;EAEpD,UAAU;GACR/C,WAAW;EACb;CACF;CAEA,MAAMgD,QACJrE,KACAZ,QACAkF,UACG;EACH,IAAI,CAACnE,WAAW,CAAC,CAAC6D,iBAAiB;EACnCvD,QAAQqB,KAAK;GACXkB,QAAQhD,IAAIgD;GACZC,QAAQjD,IAAIiD;GACZL,KAAK5C,IAAI4C;GACTxD;GACA,GAAGkF;EACL,CAAC;EACD3C,SAAS;CACX;CAEA,MAAM6C,mBAAqC;EACzCC,UAAUzE,QAAQ;GAChBQ,SAASsB,KAAK;IAAE9B;IAAKC,SAAS;GAAM,CAAC;GACrCmB,gBAAgB;GAChBQ,UAAU;GACVyC,KAAKrE,KAAK,QAAQ;EACpB;EACA0E,WAAW1E,QAAQqE,KAAKrE,KAAK,QAAQ;EACrC2E,QAAQ3E,QAAQqE,KAAKrE,KAAK,OAAO;EACjC4E,YAAY5E,QAAQ;GAClB,IAAI,CAACQ,SAASqE,MAAMC,MAAMA,EAAE9E,QAAQA,GAAG,GAAG;GAC1CQ,WAAWA,SAASuE,QAAQD,MAAMA,EAAE9E,QAAQA,GAAG;GAC/CoB,gBAAgB;GAChBQ,UAAU;EACZ;EACAoD,WAAWhF,KAAKiF,YAAY;GAC1BZ,KACErE,KACA,WACAiF,QAAQC,SAAS,aACb,EAAEX,QAAQU,QAAQE,QAAQ,IAC1B,EAAE1F,SAASH,UAAU2F,QAAQ1F,KAAK,EAAE,CAC1C;GACAmC,MAAM;EACR;CACF;CAEA,MAAM0D,aAAanB,UAAmB;EACpC,IAAI,CAAC9D,WAAW,CAAC,CAAC+D,SAAS;EAC3BvD,OAAOmB,KAAKmC,KAAK;EACjBtC,SAAS;CACX;CAEA,MAAM0D,UAAUhD,SAA6B;EAC3C,IAAIA,SAAShC,OAAO;EACpB,MAAMoD,OAAOpD;EACbA,QAAQgC;EACR,MAAMiD,aACJjD,KAAK/B,WACJ,OAAO+B,KAAKJ,UAAU,YAAYI,KAAKJ,UAAU,OAC9CzD,aAAa6D,KAAKJ,KAAK,IACvBM,KAAAA,MACJ;EACF,IAAI+C,eAAehF,QAAQ;GACzBA,QAAQiF,YAAY,IAAI;GACxBjF,SAASgF;GACThF,QAAQiF,YAAY3D,SAAS;GAC7BA,UAAU;EACZ;EACA,IAAIS,KAAKmD,aAAa/B,MAAM+B,UAAU;GACpC,IAAI/B,MAAMnF,gBAAgBmF,KAAK+B,QAAQ,CAAC,EAAEC,YAAY,IAAI;GAC1DnH,gBAAgB+D,KAAKmD,QAAQ,CAAC,EAAEC,YAAYjB,gBAAgB;GAC5D,IAAIhE,SAASqD,SAAS,GAAG;IACvBrD,WAAW,CAAA;IACXY,gBAAgB;GAClB;GACAQ,UAAU;EACZ;EACA,IAAIS,KAAKqD,SAASjC,MAAMiC,MAAM;GAC5B,IAAIjC,MAAMlF,YAAYkF,KAAKiC,IAAI,CAAC,EAAEC,QAAQ,IAAI;GAC9CpH,YAAY8D,KAAKqD,IAAI,CAAC,EAAEC,QAAQP,SAAS;EAC3C;EACA,IAAI,CAAC9E,QAAQsB,UAAU;EACvBD,SAAS;CACX;CAEA,MAAMiE,eAAe;EACnBtF,QAAQiF,YAAY,IAAI;EACxBjF,SAAS;EACT,IAAID,OAAO;GACT/B,gBAAgB+B,MAAMmF,QAAQ,CAAC,EAAEC,YAAY,IAAI;GACjDlH,YAAY8B,MAAMqF,IAAI,CAAC,EAAEC,QAAQ,IAAI;GACrCtF,QAAQ;EACV;CACF;CAqBA,OAAO;EACLgF;EACAO;EACAQ,gBAAgB;GACdlE,UAAU;GACV,OAAOpB;EACT;EACA0E,UA1Be3G,mBACdmE,QAAQC,WAAW;GAClB,IAAI,CAAC5C,OACH,OAAOyF,QAAQC,OACb,IAAIhH,mBAAmB;IAAEW,MAAM;IAAWC,OAAO;GAAU,CAAC,CAC9D;GAEF,MAAMqG,OACJ3F,MAAMmF,SAINxC;GACF,MAAMmD,UAAUH,KAAK,GAAG/C,MAAM;GAC9BvB,MAAM;GACN,OAAOyE;EACT,CAUAX;EACAa,YAAYjC,aAAyB;GACnC9C,UAAUgF,IAAIlC,QAAQ;GACtB,aAAa;IACX9C,UAAUiF,OAAOnC,QAAQ;GAC3B;EACF;CACF;AACF;AAEA,MAAaoC,oBAAmBrG,YAAAE,UAAA;CAAA,MAAAoG,IAAAC,EAAA,CAAA;CAAA,IAAAC;CAAA,IAAAF,EAAA,OAAAtG,YAAA;EAINwG,WAAMzG,WAAWC,UAAU;EAACsG,EAAA,KAAAtG;EAAAsG,EAAA,KAAAE;CAAA,OAAAA,KAAAF,EAAA;CAApD,MAAA,CAAAG,QAAexI,SAASuI,EAA4B;CAIpDC,KAAIvB,OAAQhF,KAAK;CAAC,IAAAwG;CAAA,IAAAJ,EAAA,OAAAG,QAAAH,EAAA,OAAApG,OAAA;EACRwG,WAAA;GACRD,KAAIvB,OAAQhF,KAAK;EAAC;EACnBoG,EAAA,KAAAG;EAAAH,EAAA,KAAApG;EAAAoG,EAAA,KAAAI;CAAA,OAAAA,KAAAJ,EAAA;CAFDtI,UAAU0I,EAET;CAAC,IAAAC;CAAA,IAAAC;CAAA,IAAAN,EAAA,OAAAG,MAAA;EACQE,iBAAYF,KAAIhB,OAAQ;EAAGmB,KAAA,CAACH,IAAI;EAACH,EAAA,KAAAG;EAAAH,EAAA,KAAAK;EAAAL,EAAA,KAAAM;CAAA,OAAA;EAAAD,KAAAL,EAAA;EAAAM,KAAAN,EAAA;CAAA;CAA3CtI,UAAU2I,IAA2BC,EAAM;CAAC,OACrCH;AAAI"}
|
package/dist/client/session.d.ts
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
import { StatewirePath } from "../wire/ops.js";
|
|
2
|
-
import { StatewireResponse } from "./connection.js";
|
|
3
|
-
import { StatewireSessionRecord, StatewireSessionStore } from "./sessionStore.js";
|
|
4
|
-
import { RemoteLink, StatewireWireCommand } from "../transports/sse.js";
|
|
5
|
-
import { StatewireSendError, StatewireStateSource } from "../types.js";
|
|
6
1
|
import { CommandsInvocation, CommandsListener } from "../host/host.js";
|
|
2
|
+
import { RemoteLink, StatewireRemoteProps, StatewireWireCommand } from "../transports/sse.js";
|
|
3
|
+
import { StatewireSessionRecord, StatewireSessionStore, StatewireSessionStoreOptions } from "./sessionStore.js";
|
|
4
|
+
import { Statewire, StatewireSendError } from "../types.js";
|
|
5
|
+
import { StatewirePath } from "../wire/ops.js";
|
|
7
6
|
//#region src/client/session.d.ts
|
|
8
|
-
type SessionOptions = {
|
|
9
|
-
sessionStore?: StatewireSessionStore | undefined;
|
|
10
|
-
};
|
|
11
7
|
/**
|
|
12
8
|
* queued/trying/posted: overlay live, result pending. awaiting-result: acked
|
|
13
9
|
* (overlay gone) but the result rides a late res; `posted: false` re-sends.
|
|
@@ -47,6 +43,7 @@ type Pending = {
|
|
|
47
43
|
type AttachState = "detached" | "attached" | "finished";
|
|
48
44
|
type Session = {
|
|
49
45
|
record: StatewireSessionRecord;
|
|
46
|
+
ctx: StatewireRemoteProps;
|
|
50
47
|
recordPersisted: boolean;
|
|
51
48
|
store: StatewireSessionStore;
|
|
52
49
|
lastAck: number;
|
|
@@ -54,7 +51,7 @@ type Session = {
|
|
|
54
51
|
canonical: unknown;
|
|
55
52
|
lastPublished: unknown;
|
|
56
53
|
changed: StatewirePath[];
|
|
57
|
-
source:
|
|
54
|
+
source: Statewire.StateSource;
|
|
58
55
|
attach: AttachState;
|
|
59
56
|
attachEpoch: number;
|
|
60
57
|
running: boolean;
|
|
@@ -67,22 +64,8 @@ type Session = {
|
|
|
67
64
|
send: (command: StatewireWireCommand) => Promise<unknown>;
|
|
68
65
|
commands: Record<string, (...params: unknown[]) => Promise<unknown>>;
|
|
69
66
|
};
|
|
70
|
-
declare const createSession: (options: SessionOptions) => Session;
|
|
71
|
-
declare const handleSnapshot: (s: Session, { ops, lastSeq }: {
|
|
72
|
-
ops: unknown[];
|
|
73
|
-
lastSeq: number;
|
|
74
|
-
}) => void;
|
|
75
|
-
declare const handleOps: (s: Session, { ops, res, ack }: {
|
|
76
|
-
ops: unknown[];
|
|
77
|
-
res?: StatewireResponse[];
|
|
78
|
-
ack?: number;
|
|
79
|
-
}) => void;
|
|
80
|
-
declare const handleFinish: (s: Session, { reason }: {
|
|
81
|
-
reason: string;
|
|
82
|
-
}) => void;
|
|
83
|
-
declare const start: (s: Session) => void;
|
|
84
|
-
declare const stop: (s: Session) => void;
|
|
85
67
|
declare const setTransport: (s: Session, transport: RemoteLink) => void;
|
|
68
|
+
declare const useStatewireSession: (options: StatewireSessionStoreOptions, emit: (event: unknown) => void) => Session;
|
|
86
69
|
//#endregion
|
|
87
|
-
export {
|
|
70
|
+
export { setTransport, useStatewireSession };
|
|
88
71
|
//# sourceMappingURL=session.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","names":[],"sources":["../../src/client/session.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"session.d.ts","names":[],"sources":["../../src/client/session.ts"],"mappings":";;;;;;;;;;;;KA+BK;EACC;EAAc;;EACd;EAAc;EAAwB;;EACtC;EAAc;EAAwB;;EACtC;EAAuB;EAAiB;;EACxC;EAAoB;EAAiB;;EACrC;;KAED;WACM;WACA,SAAS;WACT,KAAK;EACd,OAAO;EACP,UAAU;EACV,SAAS,OAAO;;KAmGb;KAEA;EACH,QAAQ;EACR,KAAK;EACL;EACA,OAAO;EACP;EACA,SAAS;EACT;EACA;EACA,SAAS;EACT,QAAQ,UAAU;EAClB,QAAQ;EACR;EACA;EACA;EACA,WAAW;EACX;EACA,WAAW;EACX;EACA,YAAY;EACZ,OAAO,SAAS,yBAAyB;EACzC,UAAU,mBAAmB,sBAAsB;;cAkd/C,eAAgB,GAAG,SAAS,WAAW;cA4ChC,sBACX,SAAS,8BACT,OAAO,4BACN"}
|
package/dist/client/session.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { applyOps } from "../wire/ops.js";
|
|
2
|
+
import { createMethodProxy } from "../util/methodProxy.js";
|
|
2
3
|
import { StatewireSendError } from "../types.js";
|
|
3
4
|
import { bindCommandsHost } from "../host/host.js";
|
|
4
|
-
import {
|
|
5
|
+
import { randomId } from "../util/randomId.js";
|
|
6
|
+
import { retryDelay } from "./timing.js";
|
|
5
7
|
import { createMemorySessionStore } from "./sessionStore.js";
|
|
8
|
+
import { useEffect, useState } from "@assistant-ui/tap/react-shim";
|
|
6
9
|
//#region src/client/session.ts
|
|
7
10
|
const isResolved = (state) => state.at === "settled-live" || state.at === "done";
|
|
8
11
|
const isStateSettled = (state) => state.at === "awaiting-result" || state.at === "done";
|
|
12
|
+
const inFlight = (state) => {
|
|
13
|
+
if (state.at === "queued" || state.at === "done") throw new Error(`statewire: ${state.at} entry observed after a send`);
|
|
14
|
+
return state;
|
|
15
|
+
};
|
|
9
16
|
const legalTransitions = {
|
|
10
17
|
queued: [
|
|
11
18
|
"queued",
|
|
@@ -66,27 +73,12 @@ const carried = (state) => state.at === "queued" ? {
|
|
|
66
73
|
posted: state.posted,
|
|
67
74
|
failedAttempts: state.failedAttempts
|
|
68
75
|
};
|
|
69
|
-
const newClientId = () => `c-${
|
|
76
|
+
const newClientId = () => `c-${randomId()}`;
|
|
70
77
|
const rejectedSend = (failure) => {
|
|
71
78
|
const result = Promise.reject(new StatewireSendError(failure));
|
|
72
79
|
result.catch(() => {});
|
|
73
80
|
return result;
|
|
74
81
|
};
|
|
75
|
-
const createCommandsProxy = (send) => {
|
|
76
|
-
const memo = /* @__PURE__ */ new Map();
|
|
77
|
-
return new Proxy({}, { get: (_target, method) => {
|
|
78
|
-
if (typeof method !== "string") return void 0;
|
|
79
|
-
let fn = memo.get(method);
|
|
80
|
-
if (fn === void 0) {
|
|
81
|
-
fn = (...params) => send({
|
|
82
|
-
method,
|
|
83
|
-
params
|
|
84
|
-
});
|
|
85
|
-
memo.set(method, fn);
|
|
86
|
-
}
|
|
87
|
-
return fn;
|
|
88
|
-
} });
|
|
89
|
-
};
|
|
90
82
|
const createSession = (options) => {
|
|
91
83
|
const store = options.sessionStore ?? createMemorySessionStore();
|
|
92
84
|
const stored = store.load();
|
|
@@ -95,6 +87,14 @@ const createSession = (options) => {
|
|
|
95
87
|
clientId: newClientId(),
|
|
96
88
|
nextSeq: 1
|
|
97
89
|
},
|
|
90
|
+
ctx: {
|
|
91
|
+
get clientId() {
|
|
92
|
+
return s.record.clientId;
|
|
93
|
+
},
|
|
94
|
+
onSnapshot: (args) => handleSnapshot(s, args),
|
|
95
|
+
onOps: (args) => handleOps(s, args),
|
|
96
|
+
onFinish: (args) => handleFinish(s, args)
|
|
97
|
+
},
|
|
98
98
|
recordPersisted: stored !== null,
|
|
99
99
|
store,
|
|
100
100
|
lastAck: -1,
|
|
@@ -103,7 +103,7 @@ const createSession = (options) => {
|
|
|
103
103
|
lastPublished: void 0,
|
|
104
104
|
changed: [],
|
|
105
105
|
source: {
|
|
106
|
-
|
|
106
|
+
snapshot: () => s.canonical,
|
|
107
107
|
takeChanged: () => {
|
|
108
108
|
const changed = s.changed;
|
|
109
109
|
s.changed = [];
|
|
@@ -123,7 +123,10 @@ const createSession = (options) => {
|
|
|
123
123
|
notify: () => {},
|
|
124
124
|
emitEvent: () => {},
|
|
125
125
|
send: (command) => sendCommand(s, command),
|
|
126
|
-
commands:
|
|
126
|
+
commands: createMethodProxy((method, params) => s.send({
|
|
127
|
+
method,
|
|
128
|
+
params
|
|
129
|
+
}))
|
|
127
130
|
};
|
|
128
131
|
bindCommandsHost(s.commands, { setListener: (listener) => {
|
|
129
132
|
s.lifecycle = listener;
|
|
@@ -141,6 +144,17 @@ const wake = (s) => {
|
|
|
141
144
|
for (const waiter of waiters) waiter();
|
|
142
145
|
};
|
|
143
146
|
const waitForWake = (s) => new Promise((r) => s.waiters.push(r));
|
|
147
|
+
const sleepUntilWake = (s, delayMs) => new Promise((resolve) => {
|
|
148
|
+
const waiter = () => {
|
|
149
|
+
clearTimeout(timer);
|
|
150
|
+
resolve();
|
|
151
|
+
};
|
|
152
|
+
const timer = setTimeout(() => {
|
|
153
|
+
s.waiters = s.waiters.filter((w) => w !== waiter);
|
|
154
|
+
resolve();
|
|
155
|
+
}, delayMs);
|
|
156
|
+
s.waiters.push(waiter);
|
|
157
|
+
});
|
|
144
158
|
const settleAll = (s, detach, verdict) => {
|
|
145
159
|
const cause = {
|
|
146
160
|
evicted: "session-lost",
|
|
@@ -238,11 +252,11 @@ const applyResponse = (s, response) => {
|
|
|
238
252
|
} : response.code === "invalid-params" ? {
|
|
239
253
|
fate: "invalid-params",
|
|
240
254
|
cause: "server",
|
|
241
|
-
message: response.message ?? ""
|
|
255
|
+
message: response.message ?? protocolError("rejected response missing message")
|
|
242
256
|
} : {
|
|
243
257
|
fate: "rejected",
|
|
244
258
|
cause: "server",
|
|
245
|
-
message: response.message ?? "",
|
|
259
|
+
message: response.message ?? protocolError("rejected response missing message"),
|
|
246
260
|
...response.payload !== void 0 && { payload: response.payload }
|
|
247
261
|
}
|
|
248
262
|
});
|
|
@@ -271,7 +285,7 @@ const ingestOps = (s, ops) => {
|
|
|
271
285
|
};
|
|
272
286
|
const publishIngested = (s, events) => {
|
|
273
287
|
publish(s);
|
|
274
|
-
for (const
|
|
288
|
+
for (const event of events) s.emitEvent(event);
|
|
275
289
|
wake(s);
|
|
276
290
|
};
|
|
277
291
|
const handleSnapshot = (s, { ops, lastSeq }) => {
|
|
@@ -327,7 +341,7 @@ const handleOps = (s, { ops, res, ack }) => {
|
|
|
327
341
|
publishIngested(s, events);
|
|
328
342
|
};
|
|
329
343
|
const handleFinish = (s, { reason }) => {
|
|
330
|
-
if (reason === "
|
|
344
|
+
if (reason === "evicted" || reason === "error") return;
|
|
331
345
|
s.attach = "finished";
|
|
332
346
|
settleAll(s, reason === "superseded" ? "superseded" : "gone");
|
|
333
347
|
publish(s);
|
|
@@ -363,15 +377,12 @@ const pump = async (s) => {
|
|
|
363
377
|
} catch (error) {
|
|
364
378
|
if (!s.running || isFinished(s)) return;
|
|
365
379
|
if (!s.pending.includes(entry)) continue;
|
|
366
|
-
const state = entry.state;
|
|
367
|
-
if (state.at === "queued" || state.at === "done") continue;
|
|
380
|
+
const state = inFlight(entry.state);
|
|
368
381
|
transition(s, entry, {
|
|
369
382
|
...state,
|
|
370
383
|
failedAttempts: state.failedAttempts + 1
|
|
371
384
|
});
|
|
372
|
-
|
|
373
|
-
const delay = typeof retryAfterMs === "number" ? retryAfterMs : backoff(state.failedAttempts + 1);
|
|
374
|
-
await new Promise((r) => setTimeout(r, delay));
|
|
385
|
+
await sleepUntilWake(s, retryDelay(error, state.failedAttempts + 1));
|
|
375
386
|
continue;
|
|
376
387
|
}
|
|
377
388
|
if (!s.running) return;
|
|
@@ -394,14 +405,12 @@ const pump = async (s) => {
|
|
|
394
405
|
publish(s);
|
|
395
406
|
}
|
|
396
407
|
} else if (result.type === "rejected") {
|
|
397
|
-
const { message, payload } = result;
|
|
398
408
|
transition(s, entry, { at: "done" }, {
|
|
399
409
|
ok: false,
|
|
400
410
|
failure: {
|
|
401
411
|
fate: "rejected",
|
|
402
412
|
cause: "server",
|
|
403
|
-
message
|
|
404
|
-
...payload !== void 0 && { payload }
|
|
413
|
+
message: result.message
|
|
405
414
|
}
|
|
406
415
|
});
|
|
407
416
|
publish(s);
|
|
@@ -420,13 +429,10 @@ const pump = async (s) => {
|
|
|
420
429
|
} else if (result.type === "seq-gap") {
|
|
421
430
|
if (clientId === s.record.clientId) {
|
|
422
431
|
console.error(`statewire: server reports a seq gap for seq ${entry.seq}; treating the session as corrupt and rotating the client id`);
|
|
423
|
-
const state = entry.state;
|
|
432
|
+
const state = inFlight(entry.state);
|
|
424
433
|
const failure = state.at === "awaiting-result" ? {
|
|
425
434
|
fate: "result-unavailable",
|
|
426
435
|
cause: "session-lost"
|
|
427
|
-
} : state.at === "queued" || state.at === "done" ? {
|
|
428
|
-
fate: "aborted",
|
|
429
|
-
cause: "session-lost"
|
|
430
436
|
} : {
|
|
431
437
|
fate: state.failedAttempts > 0 ? "lost" : "aborted",
|
|
432
438
|
cause: "session-lost"
|
|
@@ -503,7 +509,21 @@ const sendCommand = (s, command) => {
|
|
|
503
509
|
pump(s);
|
|
504
510
|
return result;
|
|
505
511
|
};
|
|
512
|
+
const useStatewireSession = (options, emit) => {
|
|
513
|
+
const [s] = useState(() => createSession(options));
|
|
514
|
+
useEffect(() => {
|
|
515
|
+
s.emitEvent = emit;
|
|
516
|
+
return () => {
|
|
517
|
+
s.emitEvent = () => {};
|
|
518
|
+
};
|
|
519
|
+
}, [s, emit]);
|
|
520
|
+
useEffect(() => {
|
|
521
|
+
start(s);
|
|
522
|
+
return () => stop(s);
|
|
523
|
+
}, [s]);
|
|
524
|
+
return s;
|
|
525
|
+
};
|
|
506
526
|
//#endregion
|
|
507
|
-
export {
|
|
527
|
+
export { setTransport, useStatewireSession };
|
|
508
528
|
|
|
509
529
|
//# sourceMappingURL=session.js.map
|