statewire 0.0.1 → 0.0.3
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 +12 -26
- package/dist/client/session.d.ts.map +1 -1
- package/dist/client/session.js +70 -43
- 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 +19 -35
- package/dist/host/host.d.ts.map +1 -1
- package/dist/host/host.js +86 -54
- 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 +511 -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 +54 -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 +97 -10
- package/dist/statewire.d.ts.map +1 -1
- package/dist/statewire.js +32 -36
- 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 +7 -25
- package/dist/transports/remote.js.map +1 -1
- package/dist/transports/sse.d.ts +32 -20
- package/dist/transports/sse.d.ts.map +1 -1
- package/dist/transports/sse.js +57 -46
- package/dist/transports/sse.js.map +1 -1
- package/dist/transports/testing.d.ts +8 -6
- 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 +59 -63
- package/dist/transports/ws.js.map +1 -1
- package/dist/types.d.ts +55 -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 +129 -90
- 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 +122 -110
- package/src/host/socket.ts +618 -0
- package/src/host-internal.ts +28 -0
- package/src/host.ts +72 -0
- package/src/index.ts +2 -62
- package/src/statewire.ts +168 -61
- package/src/transports/fake-sse.ts +51 -0
- package/src/transports/fixtures.ts +60 -0
- package/src/transports/remote.ts +14 -51
- package/src/transports/sse.ts +87 -89
- package/src/transports/testing.ts +33 -63
- package/src/transports/ws.ts +78 -100
- package/src/types.ts +52 -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/host/host.js
CHANGED
|
@@ -1,32 +1,43 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { applyOps, sanitizeValue } from "../wire/ops.js";
|
|
2
2
|
import { StatewireReject } from "./engine.js";
|
|
3
|
-
import { createStateDraft
|
|
3
|
+
import { createStateDraft } from "./draft.js";
|
|
4
|
+
import { createMethodProxy } from "../util/methodProxy.js";
|
|
4
5
|
import { StatewireSendError } from "../types.js";
|
|
5
6
|
import { useEffect, useState } from "@assistant-ui/tap/react-shim";
|
|
6
7
|
//#region src/host/host.ts
|
|
7
8
|
const isDev = () => typeof process === "undefined" || process.env["NODE_ENV"] !== "production";
|
|
8
9
|
const stateHosts = /* @__PURE__ */ new WeakMap();
|
|
10
|
+
/** Host-side accessor for a `useStatewireState` state object's source (`snapshot()` reads the committed state). */
|
|
9
11
|
const getStateHost = (state) => stateHosts.get(state);
|
|
10
12
|
const useStatewireState = (init) => {
|
|
11
13
|
const [, setVersion] = useState(0);
|
|
12
14
|
const [cell] = useState(() => {
|
|
15
|
+
const commit = () => {
|
|
16
|
+
if (c.draft) c.current = c.draft.flush();
|
|
17
|
+
return c.current;
|
|
18
|
+
};
|
|
13
19
|
const c = {
|
|
14
|
-
current:
|
|
20
|
+
current: sanitizeValue(init()),
|
|
15
21
|
draft: null,
|
|
16
22
|
changed: [[]],
|
|
17
23
|
onOp: null,
|
|
18
24
|
onChange: null,
|
|
19
25
|
bump: () => setVersion((v) => v + 1),
|
|
20
|
-
ctl: {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
ctl: {
|
|
27
|
+
apply: (ops) => {
|
|
28
|
+
const result = applyOps(commit(), ops);
|
|
29
|
+
if (result.events.length > 0) throw new Error("statewire: event ops do not apply to state — route them to emit");
|
|
30
|
+
c.current = result.state;
|
|
31
|
+
c.draft?.invalidate();
|
|
32
|
+
c.changed.push(...result.changedPaths);
|
|
33
|
+
if (c.onOp) for (const op of ops) c.onOp(op);
|
|
34
|
+
c.bump();
|
|
35
|
+
c.onChange?.();
|
|
36
|
+
},
|
|
37
|
+
snapshot: commit
|
|
38
|
+
},
|
|
28
39
|
host: {
|
|
29
|
-
|
|
40
|
+
snapshot: commit,
|
|
30
41
|
setOnOp: (listener) => {
|
|
31
42
|
c.onOp = listener;
|
|
32
43
|
},
|
|
@@ -42,21 +53,19 @@ const useStatewireState = (init) => {
|
|
|
42
53
|
};
|
|
43
54
|
return c;
|
|
44
55
|
});
|
|
45
|
-
let value = cell.
|
|
56
|
+
let value = cell.ctl.snapshot();
|
|
46
57
|
if (typeof value === "object" && value !== null) {
|
|
47
58
|
cell.draft ??= createStateDraft({
|
|
48
|
-
|
|
49
|
-
record: (op) => {
|
|
50
|
-
|
|
51
|
-
cell.current = applied.state;
|
|
52
|
-
cell.changed.push(...applied.changedPaths);
|
|
59
|
+
snapshot: () => cell.current,
|
|
60
|
+
record: (op, changedPath) => {
|
|
61
|
+
cell.changed.push(changedPath);
|
|
53
62
|
cell.onOp?.(op);
|
|
54
63
|
cell.bump();
|
|
55
64
|
cell.onChange?.();
|
|
56
65
|
}
|
|
57
66
|
});
|
|
58
|
-
stateHosts.set(cell.draft, cell.host);
|
|
59
|
-
value = cell.draft;
|
|
67
|
+
stateHosts.set(cell.draft.proxy, cell.host);
|
|
68
|
+
value = cell.draft.proxy;
|
|
60
69
|
}
|
|
61
70
|
return [value, cell.ctl];
|
|
62
71
|
};
|
|
@@ -124,6 +133,10 @@ const bindCommandsHost = (commands, host) => {
|
|
|
124
133
|
commandsHosts.set(commands, host);
|
|
125
134
|
};
|
|
126
135
|
const STALL_WARN_MS = 5e3;
|
|
136
|
+
const LOCAL_CALLER = Object.freeze({
|
|
137
|
+
context: null,
|
|
138
|
+
isConnected: true
|
|
139
|
+
});
|
|
127
140
|
const toSendError = (error) => error instanceof StatewireSendError ? error : new StatewireSendError({
|
|
128
141
|
fate: "rejected",
|
|
129
142
|
cause: "server",
|
|
@@ -134,22 +147,26 @@ const ackInvocation = (cell, inv) => {
|
|
|
134
147
|
if (inv.acked) return;
|
|
135
148
|
inv.acked = true;
|
|
136
149
|
cell.listener?.onAck(inv);
|
|
150
|
+
inv.origin?.onAck?.();
|
|
137
151
|
};
|
|
138
152
|
const settleInvocation = (cell, inv, call, settled) => {
|
|
139
153
|
ackInvocation(cell, inv);
|
|
140
154
|
if (settled.type === "resolved") {
|
|
141
155
|
const outcome = settled.value === void 0 ? { type: "accepted" } : {
|
|
142
156
|
type: "accepted",
|
|
143
|
-
payload:
|
|
157
|
+
payload: sanitizeValue(settled.value)
|
|
144
158
|
};
|
|
145
159
|
cell.listener?.onSettle(inv, outcome);
|
|
160
|
+
inv.origin?.onSettle?.(outcome);
|
|
146
161
|
call.resolve(outcome.payload);
|
|
147
162
|
} else {
|
|
148
163
|
const error = toSendError(settled.error);
|
|
149
|
-
|
|
164
|
+
const outcome = {
|
|
150
165
|
type: "rejected",
|
|
151
166
|
error
|
|
152
|
-
}
|
|
167
|
+
};
|
|
168
|
+
cell.listener?.onSettle(inv, outcome);
|
|
169
|
+
inv.origin?.onSettle?.(outcome);
|
|
153
170
|
call.reject(error);
|
|
154
171
|
}
|
|
155
172
|
};
|
|
@@ -162,10 +179,15 @@ const pump = (cell) => {
|
|
|
162
179
|
if (!call) return;
|
|
163
180
|
const handler = cell.handlers[call.method];
|
|
164
181
|
if (handler === void 0) {
|
|
165
|
-
|
|
182
|
+
const error = new StatewireSendError({
|
|
166
183
|
fate: "unknown-command",
|
|
167
184
|
cause: "server"
|
|
168
|
-
})
|
|
185
|
+
});
|
|
186
|
+
call.origin?.onSettle?.({
|
|
187
|
+
type: "rejected",
|
|
188
|
+
error
|
|
189
|
+
});
|
|
190
|
+
call.reject(error);
|
|
169
191
|
pump(cell);
|
|
170
192
|
return;
|
|
171
193
|
}
|
|
@@ -174,7 +196,9 @@ const pump = (cell) => {
|
|
|
174
196
|
params: call.params,
|
|
175
197
|
ts: call.ts,
|
|
176
198
|
seq: ++cell.seq,
|
|
177
|
-
acked: false
|
|
199
|
+
acked: false,
|
|
200
|
+
...call.origin !== void 0 && { origin: call.origin },
|
|
201
|
+
...call.origin?.clientId !== void 0 && { clientId: call.origin.clientId }
|
|
178
202
|
};
|
|
179
203
|
cell.running = true;
|
|
180
204
|
cell.listener?.onStart(inv);
|
|
@@ -182,7 +206,7 @@ const pump = (cell) => {
|
|
|
182
206
|
cell.listener?.onCovered?.(inv);
|
|
183
207
|
(async () => {
|
|
184
208
|
try {
|
|
185
|
-
const value = await handler(...call.params);
|
|
209
|
+
const value = await handler.call(call.origin?.caller ?? LOCAL_CALLER, ...call.params);
|
|
186
210
|
const execution = typeof value === "object" && value !== null ? executionStates.get(value) : void 0;
|
|
187
211
|
if (execution) {
|
|
188
212
|
if (execution.acked) ackInvocation(cell, inv);
|
|
@@ -225,32 +249,40 @@ const useStatewireCommands = (commands) => {
|
|
|
225
249
|
unmounted: false,
|
|
226
250
|
stallTimer: null
|
|
227
251
|
};
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (fn === void 0) {
|
|
233
|
-
fn = (...params) => new Promise((resolve, reject) => {
|
|
234
|
-
if (cell.unmounted) {
|
|
235
|
-
reject(/* @__PURE__ */ new Error("statewire: commands called after unmount"));
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
238
|
-
cell.queue.push({
|
|
239
|
-
method,
|
|
240
|
-
params,
|
|
241
|
-
ts: Date.now(),
|
|
242
|
-
resolve,
|
|
243
|
-
reject
|
|
244
|
-
});
|
|
245
|
-
pump(cell);
|
|
246
|
-
});
|
|
247
|
-
memo.set(method, fn);
|
|
252
|
+
const proxy = createMethodProxy((method, params) => new Promise((resolve, reject) => {
|
|
253
|
+
if (cell.unmounted) {
|
|
254
|
+
reject(/* @__PURE__ */ new Error("statewire: commands called after unmount"));
|
|
255
|
+
return;
|
|
248
256
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
257
|
+
cell.queue.push({
|
|
258
|
+
method,
|
|
259
|
+
params,
|
|
260
|
+
ts: Date.now(),
|
|
261
|
+
resolve,
|
|
262
|
+
reject
|
|
263
|
+
});
|
|
264
|
+
pump(cell);
|
|
265
|
+
}));
|
|
266
|
+
commandsHosts.set(proxy, {
|
|
267
|
+
setListener: (listener) => {
|
|
268
|
+
cell.listener = listener;
|
|
269
|
+
},
|
|
270
|
+
submit: (origin) => new Promise((resolve_0, reject_0) => {
|
|
271
|
+
if (cell.unmounted) {
|
|
272
|
+
reject_0(/* @__PURE__ */ new Error("statewire: commands called after unmount"));
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
cell.queue.push({
|
|
276
|
+
method: origin.method,
|
|
277
|
+
params: origin.params,
|
|
278
|
+
ts: Date.now(),
|
|
279
|
+
resolve: resolve_0,
|
|
280
|
+
reject: reject_0,
|
|
281
|
+
origin
|
|
282
|
+
});
|
|
283
|
+
pump(cell);
|
|
284
|
+
})
|
|
285
|
+
});
|
|
254
286
|
return {
|
|
255
287
|
cell,
|
|
256
288
|
proxy
|
|
@@ -281,7 +313,7 @@ const useStatewireCommands = (commands) => {
|
|
|
281
313
|
return built.proxy;
|
|
282
314
|
};
|
|
283
315
|
const emitHosts = /* @__PURE__ */ new WeakMap();
|
|
284
|
-
const getEmitHost = (emit) => emitHosts.get(emit);
|
|
316
|
+
const getEmitHost = (emit) => emit && emitHosts.get(emit);
|
|
285
317
|
const useStatewireEmit = () => {
|
|
286
318
|
const [built] = useState(_temp);
|
|
287
319
|
return built;
|
|
@@ -290,7 +322,7 @@ function _temp() {
|
|
|
290
322
|
const cell = { sink: null };
|
|
291
323
|
const emit = (event) => {
|
|
292
324
|
if (!cell.sink) throw new Error("statewire: emit before a host bound the event sink");
|
|
293
|
-
cell.sink(
|
|
325
|
+
cell.sink(sanitizeValue(event));
|
|
294
326
|
};
|
|
295
327
|
emitHosts.set(emit, { setSink: (sink) => {
|
|
296
328
|
cell.sink = sink;
|
package/dist/host/host.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.js","names":["useEffect","useState","applyOp","applyOps","StatewireOp","StatewirePath","StatewireReject","createStateDraft","frozen","ServerOp","StatewireSendError","StatewireCommands","StatewireCommandsProxy","StatewireStateSource","isDev","process","env","StatewireStateCtl","apply","ops","StateHost","setOnOp","listener","op","stateHosts","WeakMap","getStateHost","state","get","StateCell","current","draft","changed","onOp","onChange","ctl","host","bump","useStatewireState","init","TState","setVersion","cell","c","v","result","events","length","Error","push","changedPaths","root","setOnChange","takeChanged","value","record","applied","set","const","ExecutionSettled","type","error","ExecutionBind","onAck","onSettle","settled","ExecutionState","acked","bind","executionStates","executionResult","CommandExecution","T","CommandExecutionResolvers","ack","resolve","reject","Object","assign","executor","resolvers","PromiseLike","execution","freeze","settle","withResolvers","promise","Promise","res","rej","a","CommandsInvocation","method","params","ts","seq","clientId","CommandsOutcome","payload","CommandsListener","onStart","inv","onPosted","onCovered","outcome","CommandsHost","setListener","commandsHosts","getCommandsHost","commands","bindCommandsHost","QueuedCall","ActiveInvocation","CommandsCell","handlers","Record","args","queue","running","unmounted","stallTimer","ReturnType","setTimeout","STALL_WARN_MS","toSendError","fate","cause","message","String","undefined","ackInvocation","settleInvocation","call","pump","watchStall","shift","handler","console","warn","UnwrapExecution","V","StatewireHandledCommands","THandlers","Parameters","K","Awaited","useStatewireCommands","built","memo","Map","proxy","Proxy","_target","fn","Date","now","clearTimeout","queueMicrotask","queued","EmitHost","setSink","sink","event","emitHosts","getEmitHost","emit","useStatewireEmit","_temp"],"sources":["../../src/host/host.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { applyOp, applyOps } from \"../wire/ops\";\nimport type { StatewireOp, StatewirePath } from \"../wire/ops\";\nimport { StatewireReject } from \"./engine\";\nimport { createStateDraft, frozen } from \"./draft\";\nimport type { ServerOp } from \"./draft\";\nimport { StatewireSendError } from \"../types\";\nimport type {\n StatewireCommands,\n StatewireCommandsProxy,\n StatewireStateSource,\n} from \"../types\";\n\nconst isDev = () =>\n typeof process === \"undefined\" || process.env[\"NODE_ENV\"] !== \"production\";\n\n// ─── state ──────────────────────────────────────────────\n\nexport type StatewireStateCtl = {\n /** Apply server-vocabulary ops (root replace = whole-state swap). */\n apply: (ops: readonly StatewireOp[]) => void;\n};\n\nexport type StateHost = StatewireStateSource & {\n /** Synchronous, per locally-recorded op; the host owns batching. */\n setOnOp: (listener: ((op: ServerOp) => void) | null) => void;\n};\n\nconst stateHosts = new WeakMap<object, StateHost>();\n\nexport const getStateHost = (state: object): StateHost | undefined =>\n stateHosts.get(state);\n\ntype StateCell = {\n current: unknown;\n draft: object | null;\n changed: StatewirePath[];\n onOp: ((op: ServerOp) => void) | null;\n onChange: (() => void) | null;\n ctl: StatewireStateCtl;\n host: StateHost;\n bump: () => void;\n};\n\nexport const useStatewireState = <TState>(\n init: () => TState,\n): readonly [TState, StatewireStateCtl] => {\n const [, setVersion] = useState(0);\n const [cell] = useState((): StateCell => {\n const c: StateCell = {\n current: frozen(init()),\n draft: null,\n changed: [[]],\n onOp: null,\n onChange: null,\n bump: () => setVersion((v) => v + 1),\n ctl: {\n apply: (ops) => {\n const result = applyOps(c.current, ops);\n if (result.events.length > 0) {\n throw new Error(\n \"statewire: event ops do not apply to state — route them to emit\",\n );\n }\n c.current = result.state;\n c.changed.push(...result.changedPaths);\n c.bump();\n c.onChange?.();\n },\n },\n host: {\n root: () => c.current,\n setOnOp: (listener) => {\n c.onOp = listener;\n },\n setOnChange: (listener) => {\n c.onChange = listener;\n },\n takeChanged: () => {\n const changed = c.changed;\n c.changed = [];\n return changed;\n },\n },\n };\n return c;\n });\n\n let value = cell.current;\n if (typeof value === \"object\" && value !== null) {\n cell.draft ??= createStateDraft<object>({\n root: () => cell.current,\n record: (op) => {\n const applied = applyOp(cell.current, op);\n cell.current = applied.state;\n cell.changed.push(...applied.changedPaths);\n cell.onOp?.(op);\n cell.bump();\n cell.onChange?.();\n },\n });\n stateHosts.set(cell.draft, cell.host);\n value = cell.draft;\n }\n return [value as TState, cell.ctl] as const;\n};\n\n// ─── commands ───────────────────────────────────────────\n\ntype ExecutionSettled =\n | { type: \"resolved\"; value: unknown }\n | { type: \"rejected\"; error: unknown };\n\ntype ExecutionBind = {\n onAck: () => void;\n onSettle: (settled: ExecutionSettled) => void;\n};\n\ntype ExecutionState = {\n acked: boolean;\n settled: ExecutionSettled | null;\n bind: ExecutionBind | null;\n};\n\nconst executionStates = new WeakMap<object, ExecutionState>();\n\ndeclare const executionResult: unique symbol;\n\n/**\n * Handle a command handler returns to keep the command open past its own\n * return: `ack()` releases the client's optimistic overlay / ack watermark,\n * `resolve`/`reject` settle the command (and imply ack). `ack` is idempotent\n * and a no-op after settle.\n */\nexport type CommandExecution<T = void> = {\n readonly [executionResult]: (value: T) => void;\n};\n\nexport type CommandExecutionResolvers<T = void> = {\n ack: () => void;\n resolve: (value: T) => void;\n reject: (error: unknown) => void;\n};\n\n/**\n * `CommandExecution(executor)` runs the (possibly async) `executor({ ack })`\n * immediately: its return value resolves the execution, a throw/rejection\n * rejects it; `ack()` releases the client's ack watermark before the executor\n * finishes. `CommandExecution.withResolvers()` returns `[execution,\n * { ack, resolve, reject }]` instead — settle them from your own spawned work.\n */\nexport const CommandExecution = Object.assign(\n <T = void>(\n executor: (resolvers: {\n ack: () => void;\n }) => T | void | PromiseLike<T | void>,\n ): CommandExecution<T> => {\n const state: ExecutionState = { acked: false, settled: null, bind: null };\n const execution = Object.freeze({}) as unknown as CommandExecution<T>;\n executionStates.set(execution, state);\n const settle = (settled: ExecutionSettled) => {\n if (state.settled) return;\n state.settled = settled;\n state.acked = true;\n state.bind?.onSettle(settled);\n };\n const ack = () => {\n if (state.acked) return;\n state.acked = true;\n state.bind?.onAck();\n };\n void (async () => {\n try {\n settle({ type: \"resolved\", value: await executor({ ack }) });\n } catch (error) {\n settle({ type: \"rejected\", error });\n }\n })();\n return execution;\n },\n {\n withResolvers: <T = void>(): [\n CommandExecution<T>,\n CommandExecutionResolvers<T>,\n ] => {\n let resolve!: (value: T) => void;\n let reject!: (error: unknown) => void;\n const promise = new Promise<T>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n let ack!: () => void;\n const execution = CommandExecution<T>(({ ack: a }) => {\n ack = a;\n return promise;\n });\n return [execution, { ack: () => ack(), resolve, reject }];\n },\n },\n);\n\nexport type CommandsInvocation = {\n readonly method: string;\n readonly params: readonly unknown[];\n readonly ts: number;\n readonly seq: number;\n readonly clientId?: string;\n};\n\nexport type CommandsOutcome =\n | { type: \"accepted\"; payload?: unknown }\n | { type: \"rejected\"; error: unknown };\n\nexport type CommandsListener = {\n onStart: (inv: CommandsInvocation) => void;\n onPosted?: (inv: CommandsInvocation) => void;\n onAck: (inv: CommandsInvocation) => void;\n /** Canonical state now covers the invocation's effect — drop any overlay. */\n onCovered?: (inv: CommandsInvocation) => void;\n onSettle: (inv: CommandsInvocation, outcome: CommandsOutcome) => void;\n};\n\nexport type CommandsHost = {\n setListener: (listener: CommandsListener | null) => void;\n};\n\nconst commandsHosts = new WeakMap<object, CommandsHost>();\n\nexport const getCommandsHost = (commands: object): CommandsHost | undefined =>\n commandsHosts.get(commands);\n\nexport const bindCommandsHost = (commands: object, host: CommandsHost) => {\n commandsHosts.set(commands, host);\n};\n\ntype QueuedCall = {\n method: string;\n params: readonly unknown[];\n ts: number;\n resolve: (value: unknown) => void;\n reject: (error: unknown) => void;\n};\n\ntype ActiveInvocation = CommandsInvocation & { acked: boolean };\n\ntype CommandsCell = {\n handlers: Record<string, (...args: never[]) => unknown> | null;\n queue: QueuedCall[];\n running: boolean;\n listener: CommandsListener | null;\n seq: number;\n unmounted: boolean;\n stallTimer: ReturnType<typeof setTimeout> | null;\n};\n\nconst STALL_WARN_MS = 5000;\n\nconst toSendError = (error: unknown): StatewireSendError =>\n error instanceof StatewireSendError\n ? error\n : new StatewireSendError({\n fate: \"rejected\",\n cause: \"server\",\n message: error instanceof Error ? error.message : String(error),\n ...(error instanceof StatewireReject &&\n error.payload !== undefined && { payload: error.payload }),\n });\n\nconst ackInvocation = (cell: CommandsCell, inv: ActiveInvocation) => {\n if (inv.acked) return;\n inv.acked = true;\n cell.listener?.onAck(inv);\n};\n\nconst settleInvocation = (\n cell: CommandsCell,\n inv: ActiveInvocation,\n call: QueuedCall,\n settled: ExecutionSettled,\n) => {\n ackInvocation(cell, inv);\n if (settled.type === \"resolved\") {\n const outcome: CommandsOutcome =\n settled.value === undefined\n ? { type: \"accepted\" }\n : { type: \"accepted\", payload: frozen(settled.value) };\n cell.listener?.onSettle(inv, outcome);\n call.resolve(outcome.payload);\n } else {\n const error = toSendError(settled.error);\n cell.listener?.onSettle(inv, { type: \"rejected\", error });\n call.reject(error);\n }\n};\n\nconst pump = (cell: CommandsCell) => {\n if (cell.running || !cell.handlers) {\n watchStall(cell);\n return;\n }\n const call = cell.queue.shift();\n if (!call) return;\n const handler = cell.handlers[call.method];\n if (handler === undefined) {\n call.reject(\n new StatewireSendError({ fate: \"unknown-command\", cause: \"server\" }),\n );\n pump(cell);\n return;\n }\n const inv: ActiveInvocation = {\n method: call.method,\n params: call.params,\n ts: call.ts,\n seq: ++cell.seq,\n acked: false,\n };\n cell.running = true;\n cell.listener?.onStart(inv);\n cell.listener?.onPosted?.(inv);\n cell.listener?.onCovered?.(inv);\n void (async () => {\n try {\n const value = await (handler as (...args: readonly unknown[]) => unknown)(\n ...call.params,\n );\n const execution =\n typeof value === \"object\" && value !== null\n ? executionStates.get(value)\n : undefined;\n if (execution) {\n if (execution.acked) ackInvocation(cell, inv);\n if (execution.settled) {\n settleInvocation(cell, inv, call, execution.settled);\n } else {\n execution.bind = {\n onAck: () => ackInvocation(cell, inv),\n onSettle: (settled) => settleInvocation(cell, inv, call, settled),\n };\n }\n } else {\n settleInvocation(cell, inv, call, { type: \"resolved\", value });\n }\n } catch (error) {\n settleInvocation(cell, inv, call, { type: \"rejected\", error });\n } finally {\n cell.running = false;\n pump(cell);\n }\n })();\n};\n\nconst watchStall = (cell: CommandsCell) => {\n if (!isDev() || cell.stallTimer !== null || cell.queue.length === 0) return;\n if (cell.handlers) return;\n cell.stallTimer = setTimeout(() => {\n cell.stallTimer = null;\n if (cell.handlers === null && cell.queue.length > 0 && !cell.unmounted) {\n console.warn(\n `statewire: ${cell.queue.length} buffered command(s) waiting over ${STALL_WARN_MS}ms — did the host forget to arm useStatewireCommands?`,\n );\n }\n }, STALL_WARN_MS);\n};\n\ntype UnwrapExecution<V> = V extends CommandExecution<infer T> ? T : V;\n\nexport type StatewireHandledCommands<THandlers extends StatewireCommands> = {\n [K in keyof THandlers]: (\n ...args: Parameters<THandlers[K]>\n ) => UnwrapExecution<Awaited<ReturnType<THandlers[K]>>>;\n};\n\nexport const useStatewireCommands = <THandlers extends StatewireCommands>(\n commands: THandlers | undefined | null | false,\n): StatewireCommandsProxy<StatewireHandledCommands<THandlers>> => {\n const [built] = useState(() => {\n const cell: CommandsCell = {\n handlers: null,\n queue: [],\n running: false,\n listener: null,\n seq: 0,\n unmounted: false,\n stallTimer: null,\n };\n const memo = new Map<string, (...params: unknown[]) => Promise<unknown>>();\n const proxy = new Proxy(\n {} as StatewireCommandsProxy<StatewireHandledCommands<THandlers>>,\n {\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 new Promise((resolve, reject) => {\n if (cell.unmounted) {\n reject(new Error(\"statewire: commands called after unmount\"));\n return;\n }\n cell.queue.push({\n method,\n params,\n ts: Date.now(),\n resolve,\n reject,\n });\n pump(cell);\n });\n memo.set(method, fn);\n }\n return fn;\n },\n },\n );\n commandsHosts.set(proxy, {\n setListener: (listener) => {\n cell.listener = listener;\n },\n });\n return { cell, proxy };\n });\n\n useEffect(() => {\n const { cell } = built;\n cell.unmounted = false;\n return () => {\n cell.unmounted = true;\n cell.handlers = null;\n if (cell.stallTimer !== null) {\n clearTimeout(cell.stallTimer);\n cell.stallTimer = null;\n }\n queueMicrotask(() => {\n if (!cell.unmounted) return;\n const queued = cell.queue;\n cell.queue = [];\n for (const call of queued) {\n call.reject(new Error(\"statewire: unmounted\"));\n }\n });\n };\n }, [built]);\n\n useEffect(() => {\n built.cell.handlers = commands || null;\n pump(built.cell);\n });\n\n return built.proxy;\n};\n\n// ─── emit ───────────────────────────────────────────────\n\nexport type EmitHost = {\n setSink: (sink: ((event: unknown) => void) | null) => void;\n};\n\nconst emitHosts = new WeakMap<object, EmitHost>();\n\nexport const getEmitHost = (emit: object): EmitHost | undefined =>\n emitHosts.get(emit);\n\nexport const useStatewireEmit = <TEvent>(): ((event: TEvent) => void) => {\n const [built] = useState(() => {\n const cell = { sink: null as ((event: unknown) => void) | null };\n const emit = (event: TEvent) => {\n if (!cell.sink) {\n throw new Error(\"statewire: emit before a host bound the event sink\");\n }\n cell.sink(frozen(event));\n };\n emitHosts.set(emit, {\n setSink: (sink) => {\n cell.sink = sink;\n },\n });\n return emit;\n });\n return built;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"host.js","names":["useEffect","useState","applyOps","sanitizeValue","StatewirePath","StatewireReject","createStateDraft","createMethodProxy","ServerOp","StateDraft","StatewireSendError","Statewire","StatewireHost","isDev","process","env","stateHosts","WeakMap","StateHost","getStateHost","state","get","StateCell","current","draft","changed","onOp","op","onChange","ctl","StateCtl","host","bump","useStatewireState","init","TState","setVersion","cell","commit","c","flush","v","apply","ops","result","events","length","Error","invalidate","push","changedPaths","snapshot","setOnOp","listener","setOnChange","takeChanged","value","record","changedPath","set","proxy","const","ExecutionSettled","type","error","ExecutionBind","onAck","onSettle","settled","ExecutionState","acked","bind","executionStates","CommandExecution","Object","assign","executor","resolvers","ack","T","PromiseLike","Execution","execution","freeze","settle","withResolvers","ExecutionResolvers","resolve","reject","promise","Promise","res","rej","a","CommandsInvocation","method","params","ts","seq","clientId","CommandsOutcome","payload","CommandsListener","onStart","inv","onPosted","onCovered","outcome","CommandsSubmission","caller","Caller","CommandsHost","setListener","submit","call","commandsHosts","getCommandsHost","commands","bindCommandsHost","QueuedCall","origin","ActiveInvocation","CommandsCell","handlers","Record","args","queue","running","unmounted","stallTimer","ReturnType","setTimeout","STALL_WARN_MS","LOCAL_CALLER","context","isConnected","toSendError","fate","cause","message","String","undefined","ackInvocation","settleInvocation","pump","watchStall","shift","handler","console","warn","useStatewireCommands","Commands","THandlers","ThisType","CommandsProxy","HandledCommands","built","Date","now","clearTimeout","queueMicrotask","queued","EmitHost","setSink","sink","event","emitHosts","getEmitHost","emit","useStatewireEmit","_temp"],"sources":["../../src/host/host.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { applyOps, sanitizeValue } from \"../wire/ops\";\nimport type { StatewirePath } from \"../wire/ops\";\nimport { StatewireReject } from \"./engine\";\nimport { createStateDraft } from \"./draft\";\nimport { createMethodProxy } from \"../util/methodProxy\";\nimport type { ServerOp, StateDraft } from \"./draft\";\nimport { StatewireSendError } from \"../types\";\nimport type { Statewire } from \"../types\";\nimport type { StatewireHost } from \"../host\";\n\nconst isDev = () =>\n typeof process === \"undefined\" || process.env[\"NODE_ENV\"] !== \"production\";\n\n// ─── state ──────────────────────────────────────────────\n\nconst stateHosts = new WeakMap<object, StatewireHost.StateHost>();\n\n/** Host-side accessor for a `useStatewireState` state object's source (`snapshot()` reads the committed state). */\nexport const getStateHost = (\n state: object,\n): StatewireHost.StateHost | undefined => stateHosts.get(state);\n\ntype StateCell = {\n current: unknown;\n draft: StateDraft<object> | null;\n changed: StatewirePath[];\n onOp: ((op: ServerOp) => void) | null;\n onChange: (() => void) | null;\n ctl: StatewireHost.StateCtl;\n host: StatewireHost.StateHost;\n bump: () => void;\n};\n\nexport const useStatewireState = <TState>(\n init: () => TState,\n): readonly [TState, StatewireHost.StateCtl<TState>] => {\n const [, setVersion] = useState(0);\n const [cell] = useState((): StateCell => {\n const commit = () => {\n if (c.draft) c.current = c.draft.flush();\n return c.current;\n };\n const c: StateCell = {\n current: sanitizeValue(init()),\n draft: null,\n changed: [[]],\n onOp: null,\n onChange: null,\n bump: () => setVersion((v) => v + 1),\n ctl: {\n apply: (ops) => {\n const result = applyOps(commit(), ops);\n if (result.events.length > 0) {\n throw new Error(\n \"statewire: event ops do not apply to state — route them to emit\",\n );\n }\n c.current = result.state;\n c.draft?.invalidate();\n c.changed.push(...result.changedPaths);\n if (c.onOp) for (const op of ops) c.onOp(op as ServerOp);\n c.bump();\n c.onChange?.();\n },\n snapshot: commit,\n },\n host: {\n snapshot: commit,\n setOnOp: (listener) => {\n c.onOp = listener;\n },\n setOnChange: (listener) => {\n c.onChange = listener;\n },\n takeChanged: () => {\n const changed = c.changed;\n c.changed = [];\n return changed;\n },\n },\n };\n return c;\n });\n\n let value = cell.ctl.snapshot();\n if (typeof value === \"object\" && value !== null) {\n cell.draft ??= createStateDraft<object>({\n snapshot: () => cell.current,\n record: (op, changedPath) => {\n cell.changed.push(changedPath);\n cell.onOp?.(op);\n cell.bump();\n cell.onChange?.();\n },\n });\n stateHosts.set(cell.draft.proxy, cell.host);\n value = cell.draft.proxy;\n }\n return [value as TState, cell.ctl as StatewireHost.StateCtl<TState>] as const;\n};\n\n// ─── commands ───────────────────────────────────────────\n\ntype ExecutionSettled =\n | { type: \"resolved\"; value: unknown }\n | { type: \"rejected\"; error: unknown };\n\ntype ExecutionBind = {\n onAck: () => void;\n onSettle: (settled: ExecutionSettled) => void;\n};\n\ntype ExecutionState = {\n acked: boolean;\n settled: ExecutionSettled | null;\n bind: ExecutionBind | null;\n};\n\nconst executionStates = new WeakMap<object, ExecutionState>();\n\n/**\n * `CommandExecution(executor)` runs the (possibly async) `executor({ ack })`\n * immediately: its return value resolves the execution, a throw/rejection\n * rejects it; `ack()` releases the client's ack watermark before the executor\n * finishes. `CommandExecution.withResolvers()` returns `[execution,\n * { ack, resolve, reject }]` instead — settle them from your own spawned work.\n */\nexport const CommandExecution = Object.assign(\n <T = void>(\n executor: (resolvers: {\n ack: () => void;\n }) => T | void | PromiseLike<T | void>,\n ): StatewireHost.Execution<T> => {\n const state: ExecutionState = { acked: false, settled: null, bind: null };\n const execution = Object.freeze(\n {},\n ) as unknown as StatewireHost.Execution<T>;\n executionStates.set(execution, state);\n const settle = (settled: ExecutionSettled) => {\n if (state.settled) return;\n state.settled = settled;\n state.acked = true;\n state.bind?.onSettle(settled);\n };\n const ack = () => {\n if (state.acked) return;\n state.acked = true;\n state.bind?.onAck();\n };\n void (async () => {\n try {\n settle({ type: \"resolved\", value: await executor({ ack }) });\n } catch (error) {\n settle({ type: \"rejected\", error });\n }\n })();\n return execution;\n },\n {\n withResolvers: <T = void>(): [\n StatewireHost.Execution<T>,\n StatewireHost.ExecutionResolvers<T>,\n ] => {\n let resolve!: (value: T) => void;\n let reject!: (error: unknown) => void;\n const promise = new Promise<T>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n let ack!: () => void;\n const execution = CommandExecution<T>(({ ack: a }) => {\n ack = a;\n return promise;\n });\n return [execution, { ack: () => ack(), resolve, reject }];\n },\n },\n);\n\nexport type CommandsInvocation = {\n readonly method: string;\n readonly params: readonly unknown[];\n readonly ts: number;\n readonly seq: number;\n readonly clientId?: string;\n};\n\nexport type CommandsOutcome =\n | { type: \"accepted\"; payload?: unknown }\n | { type: \"rejected\"; error: unknown };\n\nexport type CommandsListener = {\n onStart: (inv: CommandsInvocation) => void;\n onPosted?: (inv: CommandsInvocation) => void;\n onAck: (inv: CommandsInvocation) => void;\n /** Canonical state now covers the invocation's effect — drop any overlay. */\n onCovered?: (inv: CommandsInvocation) => void;\n onSettle: (inv: CommandsInvocation, outcome: CommandsOutcome) => void;\n};\n\nexport type CommandsSubmission = {\n method: string;\n params: readonly unknown[];\n clientId?: string;\n caller?: StatewireHost.Caller;\n onAck?: () => void;\n onSettle?: (outcome: CommandsOutcome) => void;\n};\n\nexport type CommandsHost = {\n setListener: (listener: CommandsListener | null) => void;\n submit?: (call: CommandsSubmission) => Promise<unknown>;\n};\n\nconst commandsHosts = new WeakMap<object, CommandsHost>();\n\nexport const getCommandsHost = (commands: object): CommandsHost | undefined =>\n commandsHosts.get(commands);\n\nexport const bindCommandsHost = (commands: object, host: CommandsHost) => {\n commandsHosts.set(commands, host);\n};\n\ntype QueuedCall = {\n method: string;\n params: readonly unknown[];\n ts: number;\n resolve: (value: unknown) => void;\n reject: (error: unknown) => void;\n origin?: CommandsSubmission;\n};\n\ntype ActiveInvocation = CommandsInvocation & {\n acked: boolean;\n origin?: CommandsSubmission;\n};\n\ntype CommandsCell = {\n handlers: Record<string, (...args: never[]) => unknown> | null;\n queue: QueuedCall[];\n running: boolean;\n listener: CommandsListener | null;\n seq: number;\n unmounted: boolean;\n stallTimer: ReturnType<typeof setTimeout> | null;\n};\n\nconst STALL_WARN_MS = 5000;\n\n// In-process dispatch has no wire client: never any ambient context, trivially attached.\nconst LOCAL_CALLER: StatewireHost.Caller = Object.freeze({\n context: null,\n isConnected: true,\n});\n\nconst toSendError = (error: unknown): StatewireSendError =>\n error instanceof StatewireSendError\n ? error\n : new StatewireSendError({\n fate: \"rejected\",\n cause: \"server\",\n message: error instanceof Error ? error.message : String(error),\n ...(error instanceof StatewireReject &&\n error.payload !== undefined && { payload: error.payload }),\n });\n\nconst ackInvocation = (cell: CommandsCell, inv: ActiveInvocation) => {\n if (inv.acked) return;\n inv.acked = true;\n cell.listener?.onAck(inv);\n inv.origin?.onAck?.();\n};\n\nconst settleInvocation = (\n cell: CommandsCell,\n inv: ActiveInvocation,\n call: QueuedCall,\n settled: ExecutionSettled,\n) => {\n ackInvocation(cell, inv);\n if (settled.type === \"resolved\") {\n const outcome: CommandsOutcome =\n settled.value === undefined\n ? { type: \"accepted\" }\n : { type: \"accepted\", payload: sanitizeValue(settled.value) };\n cell.listener?.onSettle(inv, outcome);\n inv.origin?.onSettle?.(outcome);\n call.resolve(outcome.payload);\n } else {\n const error = toSendError(settled.error);\n const outcome: CommandsOutcome = { type: \"rejected\", error };\n cell.listener?.onSettle(inv, outcome);\n inv.origin?.onSettle?.(outcome);\n call.reject(error);\n }\n};\n\nconst pump = (cell: CommandsCell) => {\n if (cell.running || !cell.handlers) {\n watchStall(cell);\n return;\n }\n const call = cell.queue.shift();\n if (!call) return;\n const handler = cell.handlers[call.method];\n if (handler === undefined) {\n const error = new StatewireSendError({\n fate: \"unknown-command\",\n cause: \"server\",\n });\n call.origin?.onSettle?.({ type: \"rejected\", error });\n call.reject(error);\n pump(cell);\n return;\n }\n const inv: ActiveInvocation = {\n method: call.method,\n params: call.params,\n ts: call.ts,\n seq: ++cell.seq,\n acked: false,\n ...(call.origin !== undefined && { origin: call.origin }),\n ...(call.origin?.clientId !== undefined && {\n clientId: call.origin.clientId,\n }),\n };\n cell.running = true;\n cell.listener?.onStart(inv);\n cell.listener?.onPosted?.(inv);\n cell.listener?.onCovered?.(inv);\n void (async () => {\n try {\n const value = await (\n handler as (...args: readonly unknown[]) => unknown\n ).call(call.origin?.caller ?? LOCAL_CALLER, ...call.params);\n const execution =\n typeof value === \"object\" && value !== null\n ? executionStates.get(value)\n : undefined;\n if (execution) {\n if (execution.acked) ackInvocation(cell, inv);\n if (execution.settled) {\n settleInvocation(cell, inv, call, execution.settled);\n } else {\n execution.bind = {\n onAck: () => ackInvocation(cell, inv),\n onSettle: (settled) => settleInvocation(cell, inv, call, settled),\n };\n }\n } else {\n settleInvocation(cell, inv, call, { type: \"resolved\", value });\n }\n } catch (error) {\n settleInvocation(cell, inv, call, { type: \"rejected\", error });\n } finally {\n cell.running = false;\n pump(cell);\n }\n })();\n};\n\nconst watchStall = (cell: CommandsCell) => {\n if (!isDev() || cell.stallTimer !== null || cell.queue.length === 0) return;\n if (cell.handlers) return;\n cell.stallTimer = setTimeout(() => {\n cell.stallTimer = null;\n if (cell.handlers === null && cell.queue.length > 0 && !cell.unmounted) {\n console.warn(\n `statewire: ${cell.queue.length} buffered command(s) waiting over ${STALL_WARN_MS}ms — did the host forget to arm useStatewireCommands?`,\n );\n }\n }, STALL_WARN_MS);\n};\n\nexport const useStatewireCommands = <THandlers extends Statewire.Commands>(\n commands:\n | (THandlers & ThisType<StatewireHost.Caller>)\n | undefined\n | null\n | false,\n): Statewire.CommandsProxy<StatewireHost.HandledCommands<THandlers>> => {\n const [built] = useState(() => {\n const cell: CommandsCell = {\n handlers: null,\n queue: [],\n running: false,\n listener: null,\n seq: 0,\n unmounted: false,\n stallTimer: null,\n };\n const proxy = createMethodProxy<\n Statewire.CommandsProxy<StatewireHost.HandledCommands<THandlers>>\n >(\n (method, params) =>\n new Promise((resolve, reject) => {\n if (cell.unmounted) {\n reject(new Error(\"statewire: commands called after unmount\"));\n return;\n }\n cell.queue.push({\n method,\n params,\n ts: Date.now(),\n resolve,\n reject,\n });\n pump(cell);\n }),\n );\n commandsHosts.set(proxy, {\n setListener: (listener) => {\n cell.listener = listener;\n },\n submit: (origin) =>\n new Promise((resolve, reject) => {\n if (cell.unmounted) {\n reject(new Error(\"statewire: commands called after unmount\"));\n return;\n }\n cell.queue.push({\n method: origin.method,\n params: origin.params,\n ts: Date.now(),\n resolve,\n reject,\n origin,\n });\n pump(cell);\n }),\n });\n return { cell, proxy };\n });\n\n useEffect(() => {\n const { cell } = built;\n cell.unmounted = false;\n return () => {\n cell.unmounted = true;\n cell.handlers = null;\n if (cell.stallTimer !== null) {\n clearTimeout(cell.stallTimer);\n cell.stallTimer = null;\n }\n queueMicrotask(() => {\n if (!cell.unmounted) return;\n const queued = cell.queue;\n cell.queue = [];\n for (const call of queued) {\n call.reject(new Error(\"statewire: unmounted\"));\n }\n });\n };\n }, [built]);\n\n useEffect(() => {\n built.cell.handlers = commands || null;\n pump(built.cell);\n });\n\n return built.proxy;\n};\n\n// ─── emit ───────────────────────────────────────────────\n\nexport type EmitHost = {\n setSink: (sink: ((event: unknown) => void) | null) => void;\n};\n\nconst emitHosts = new WeakMap<object, EmitHost>();\n\nexport const getEmitHost = (emit: object | undefined): EmitHost | undefined =>\n emit && emitHosts.get(emit);\n\nexport const useStatewireEmit = <TEvent>(): ((event: TEvent) => void) => {\n const [built] = useState(() => {\n const cell = { sink: null as ((event: unknown) => void) | null };\n const emit = (event: TEvent) => {\n if (!cell.sink) {\n throw new Error(\"statewire: emit before a host bound the event sink\");\n }\n cell.sink(sanitizeValue(event));\n };\n emitHosts.set(emit, {\n setSink: (sink) => {\n cell.sink = sink;\n },\n });\n return emit;\n });\n return built;\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Statewire } from "../types.js";
|
|
2
|
+
import { StatewireHostInternal } from "../host-internal.js";
|
|
3
|
+
import { ResourceElement } from "@assistant-ui/tap";
|
|
4
|
+
//#region src/host/socket.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Transport-agnostic WS binding over a statewire/host transport:
|
|
7
|
+
* feed it sockets (anything with `send`/`close`) and raw text frames, and it
|
|
8
|
+
* speaks the Statewire envelope protocol — snapshot-first attach with `syn`,
|
|
9
|
+
* delta/ack/res envelopes, per-client-id supersede, and `fin` on finish.
|
|
10
|
+
*/
|
|
11
|
+
declare const StatewireSocketHost: (transport: ResourceElement<Statewire>, options?: StatewireHostInternal.SocketHostOptions) => StatewireHostInternal.SocketHost;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { StatewireSocketHost };
|
|
14
|
+
//# sourceMappingURL=socket.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket.d.ts","names":[],"sources":["../../src/host/socket.ts"],"mappings":";;;;;;;;;;cAulBa,sBACX,WAAW,gBAAgB,YAC3B,UAAU,sBAAsB,sBAC/B,sBAAsB"}
|