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/src/host/host.ts
CHANGED
|
@@ -1,54 +1,48 @@
|
|
|
1
1
|
import { useEffect, useState } from "react";
|
|
2
|
-
import {
|
|
3
|
-
import type {
|
|
2
|
+
import { applyOps, sanitizeValue } from "../wire/ops";
|
|
3
|
+
import type { StatewirePath } from "../wire/ops";
|
|
4
4
|
import { StatewireReject } from "./engine";
|
|
5
|
-
import { createStateDraft
|
|
6
|
-
import
|
|
5
|
+
import { createStateDraft } from "./draft";
|
|
6
|
+
import { createMethodProxy } from "../util/methodProxy";
|
|
7
|
+
import type { ServerOp, StateDraft } from "./draft";
|
|
7
8
|
import { StatewireSendError } from "../types";
|
|
8
|
-
import type {
|
|
9
|
-
|
|
10
|
-
StatewireCommandsProxy,
|
|
11
|
-
StatewireStateSource,
|
|
12
|
-
} from "../types";
|
|
9
|
+
import type { Statewire } from "../types";
|
|
10
|
+
import type { StatewireHost } from "../host";
|
|
13
11
|
|
|
14
12
|
const isDev = () =>
|
|
15
13
|
typeof process === "undefined" || process.env["NODE_ENV"] !== "production";
|
|
16
14
|
|
|
17
15
|
// ─── state ──────────────────────────────────────────────
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
/** Apply server-vocabulary ops (root replace = whole-state swap). */
|
|
21
|
-
apply: (ops: readonly StatewireOp[]) => void;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export type StateHost = StatewireStateSource & {
|
|
25
|
-
/** Synchronous, per locally-recorded op; the host owns batching. */
|
|
26
|
-
setOnOp: (listener: ((op: ServerOp) => void) | null) => void;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const stateHosts = new WeakMap<object, StateHost>();
|
|
17
|
+
const stateHosts = new WeakMap<object, StatewireHost.StateHost>();
|
|
30
18
|
|
|
31
|
-
|
|
32
|
-
|
|
19
|
+
/** Host-side accessor for a `useStatewireState` state object's source (`snapshot()` reads the committed state). */
|
|
20
|
+
export const getStateHost = (
|
|
21
|
+
state: object,
|
|
22
|
+
): StatewireHost.StateHost | undefined => stateHosts.get(state);
|
|
33
23
|
|
|
34
24
|
type StateCell = {
|
|
35
25
|
current: unknown;
|
|
36
|
-
draft: object | null;
|
|
26
|
+
draft: StateDraft<object> | null;
|
|
37
27
|
changed: StatewirePath[];
|
|
38
28
|
onOp: ((op: ServerOp) => void) | null;
|
|
39
29
|
onChange: (() => void) | null;
|
|
40
|
-
ctl:
|
|
41
|
-
host: StateHost;
|
|
30
|
+
ctl: StatewireHost.StateCtl;
|
|
31
|
+
host: StatewireHost.StateHost;
|
|
42
32
|
bump: () => void;
|
|
43
33
|
};
|
|
44
34
|
|
|
45
35
|
export const useStatewireState = <TState>(
|
|
46
36
|
init: () => TState,
|
|
47
|
-
): readonly [TState,
|
|
37
|
+
): readonly [TState, StatewireHost.StateCtl<TState>] => {
|
|
48
38
|
const [, setVersion] = useState(0);
|
|
49
39
|
const [cell] = useState((): StateCell => {
|
|
40
|
+
const commit = () => {
|
|
41
|
+
if (c.draft) c.current = c.draft.flush();
|
|
42
|
+
return c.current;
|
|
43
|
+
};
|
|
50
44
|
const c: StateCell = {
|
|
51
|
-
current:
|
|
45
|
+
current: sanitizeValue(init()),
|
|
52
46
|
draft: null,
|
|
53
47
|
changed: [[]],
|
|
54
48
|
onOp: null,
|
|
@@ -56,20 +50,23 @@ export const useStatewireState = <TState>(
|
|
|
56
50
|
bump: () => setVersion((v) => v + 1),
|
|
57
51
|
ctl: {
|
|
58
52
|
apply: (ops) => {
|
|
59
|
-
const result = applyOps(
|
|
53
|
+
const result = applyOps(commit(), ops);
|
|
60
54
|
if (result.events.length > 0) {
|
|
61
55
|
throw new Error(
|
|
62
56
|
"statewire: event ops do not apply to state — route them to emit",
|
|
63
57
|
);
|
|
64
58
|
}
|
|
65
59
|
c.current = result.state;
|
|
60
|
+
c.draft?.invalidate();
|
|
66
61
|
c.changed.push(...result.changedPaths);
|
|
62
|
+
if (c.onOp) for (const op of ops) c.onOp(op as ServerOp);
|
|
67
63
|
c.bump();
|
|
68
64
|
c.onChange?.();
|
|
69
65
|
},
|
|
66
|
+
snapshot: commit,
|
|
70
67
|
},
|
|
71
68
|
host: {
|
|
72
|
-
|
|
69
|
+
snapshot: commit,
|
|
73
70
|
setOnOp: (listener) => {
|
|
74
71
|
c.onOp = listener;
|
|
75
72
|
},
|
|
@@ -86,23 +83,21 @@ export const useStatewireState = <TState>(
|
|
|
86
83
|
return c;
|
|
87
84
|
});
|
|
88
85
|
|
|
89
|
-
let value = cell.
|
|
86
|
+
let value = cell.ctl.snapshot();
|
|
90
87
|
if (typeof value === "object" && value !== null) {
|
|
91
88
|
cell.draft ??= createStateDraft<object>({
|
|
92
|
-
|
|
93
|
-
record: (op) => {
|
|
94
|
-
|
|
95
|
-
cell.current = applied.state;
|
|
96
|
-
cell.changed.push(...applied.changedPaths);
|
|
89
|
+
snapshot: () => cell.current,
|
|
90
|
+
record: (op, changedPath) => {
|
|
91
|
+
cell.changed.push(changedPath);
|
|
97
92
|
cell.onOp?.(op);
|
|
98
93
|
cell.bump();
|
|
99
94
|
cell.onChange?.();
|
|
100
95
|
},
|
|
101
96
|
});
|
|
102
|
-
stateHosts.set(cell.draft, cell.host);
|
|
103
|
-
value = cell.draft;
|
|
97
|
+
stateHosts.set(cell.draft.proxy, cell.host);
|
|
98
|
+
value = cell.draft.proxy;
|
|
104
99
|
}
|
|
105
|
-
return [value as TState, cell.ctl] as const;
|
|
100
|
+
return [value as TState, cell.ctl as StatewireHost.StateCtl<TState>] as const;
|
|
106
101
|
};
|
|
107
102
|
|
|
108
103
|
// ─── commands ───────────────────────────────────────────
|
|
@@ -124,24 +119,6 @@ type ExecutionState = {
|
|
|
124
119
|
|
|
125
120
|
const executionStates = new WeakMap<object, ExecutionState>();
|
|
126
121
|
|
|
127
|
-
declare const executionResult: unique symbol;
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Handle a command handler returns to keep the command open past its own
|
|
131
|
-
* return: `ack()` releases the client's optimistic overlay / ack watermark,
|
|
132
|
-
* `resolve`/`reject` settle the command (and imply ack). `ack` is idempotent
|
|
133
|
-
* and a no-op after settle.
|
|
134
|
-
*/
|
|
135
|
-
export type CommandExecution<T = void> = {
|
|
136
|
-
readonly [executionResult]: (value: T) => void;
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
export type CommandExecutionResolvers<T = void> = {
|
|
140
|
-
ack: () => void;
|
|
141
|
-
resolve: (value: T) => void;
|
|
142
|
-
reject: (error: unknown) => void;
|
|
143
|
-
};
|
|
144
|
-
|
|
145
122
|
/**
|
|
146
123
|
* `CommandExecution(executor)` runs the (possibly async) `executor({ ack })`
|
|
147
124
|
* immediately: its return value resolves the execution, a throw/rejection
|
|
@@ -154,9 +131,11 @@ export const CommandExecution = Object.assign(
|
|
|
154
131
|
executor: (resolvers: {
|
|
155
132
|
ack: () => void;
|
|
156
133
|
}) => T | void | PromiseLike<T | void>,
|
|
157
|
-
):
|
|
134
|
+
): StatewireHost.Execution<T> => {
|
|
158
135
|
const state: ExecutionState = { acked: false, settled: null, bind: null };
|
|
159
|
-
const execution = Object.freeze(
|
|
136
|
+
const execution = Object.freeze(
|
|
137
|
+
{},
|
|
138
|
+
) as unknown as StatewireHost.Execution<T>;
|
|
160
139
|
executionStates.set(execution, state);
|
|
161
140
|
const settle = (settled: ExecutionSettled) => {
|
|
162
141
|
if (state.settled) return;
|
|
@@ -180,8 +159,8 @@ export const CommandExecution = Object.assign(
|
|
|
180
159
|
},
|
|
181
160
|
{
|
|
182
161
|
withResolvers: <T = void>(): [
|
|
183
|
-
|
|
184
|
-
|
|
162
|
+
StatewireHost.Execution<T>,
|
|
163
|
+
StatewireHost.ExecutionResolvers<T>,
|
|
185
164
|
] => {
|
|
186
165
|
let resolve!: (value: T) => void;
|
|
187
166
|
let reject!: (error: unknown) => void;
|
|
@@ -220,8 +199,18 @@ export type CommandsListener = {
|
|
|
220
199
|
onSettle: (inv: CommandsInvocation, outcome: CommandsOutcome) => void;
|
|
221
200
|
};
|
|
222
201
|
|
|
202
|
+
export type CommandsSubmission = {
|
|
203
|
+
method: string;
|
|
204
|
+
params: readonly unknown[];
|
|
205
|
+
clientId?: string;
|
|
206
|
+
caller?: StatewireHost.Caller;
|
|
207
|
+
onAck?: () => void;
|
|
208
|
+
onSettle?: (outcome: CommandsOutcome) => void;
|
|
209
|
+
};
|
|
210
|
+
|
|
223
211
|
export type CommandsHost = {
|
|
224
212
|
setListener: (listener: CommandsListener | null) => void;
|
|
213
|
+
submit?: (call: CommandsSubmission) => Promise<unknown>;
|
|
225
214
|
};
|
|
226
215
|
|
|
227
216
|
const commandsHosts = new WeakMap<object, CommandsHost>();
|
|
@@ -239,9 +228,13 @@ type QueuedCall = {
|
|
|
239
228
|
ts: number;
|
|
240
229
|
resolve: (value: unknown) => void;
|
|
241
230
|
reject: (error: unknown) => void;
|
|
231
|
+
origin?: CommandsSubmission;
|
|
242
232
|
};
|
|
243
233
|
|
|
244
|
-
type ActiveInvocation = CommandsInvocation & {
|
|
234
|
+
type ActiveInvocation = CommandsInvocation & {
|
|
235
|
+
acked: boolean;
|
|
236
|
+
origin?: CommandsSubmission;
|
|
237
|
+
};
|
|
245
238
|
|
|
246
239
|
type CommandsCell = {
|
|
247
240
|
handlers: Record<string, (...args: never[]) => unknown> | null;
|
|
@@ -255,6 +248,12 @@ type CommandsCell = {
|
|
|
255
248
|
|
|
256
249
|
const STALL_WARN_MS = 5000;
|
|
257
250
|
|
|
251
|
+
// In-process dispatch has no wire client: never any ambient context, trivially attached.
|
|
252
|
+
const LOCAL_CALLER: StatewireHost.Caller = Object.freeze({
|
|
253
|
+
context: null,
|
|
254
|
+
isConnected: true,
|
|
255
|
+
});
|
|
256
|
+
|
|
258
257
|
const toSendError = (error: unknown): StatewireSendError =>
|
|
259
258
|
error instanceof StatewireSendError
|
|
260
259
|
? error
|
|
@@ -270,6 +269,7 @@ const ackInvocation = (cell: CommandsCell, inv: ActiveInvocation) => {
|
|
|
270
269
|
if (inv.acked) return;
|
|
271
270
|
inv.acked = true;
|
|
272
271
|
cell.listener?.onAck(inv);
|
|
272
|
+
inv.origin?.onAck?.();
|
|
273
273
|
};
|
|
274
274
|
|
|
275
275
|
const settleInvocation = (
|
|
@@ -283,12 +283,15 @@ const settleInvocation = (
|
|
|
283
283
|
const outcome: CommandsOutcome =
|
|
284
284
|
settled.value === undefined
|
|
285
285
|
? { type: "accepted" }
|
|
286
|
-
: { type: "accepted", payload:
|
|
286
|
+
: { type: "accepted", payload: sanitizeValue(settled.value) };
|
|
287
287
|
cell.listener?.onSettle(inv, outcome);
|
|
288
|
+
inv.origin?.onSettle?.(outcome);
|
|
288
289
|
call.resolve(outcome.payload);
|
|
289
290
|
} else {
|
|
290
291
|
const error = toSendError(settled.error);
|
|
291
|
-
|
|
292
|
+
const outcome: CommandsOutcome = { type: "rejected", error };
|
|
293
|
+
cell.listener?.onSettle(inv, outcome);
|
|
294
|
+
inv.origin?.onSettle?.(outcome);
|
|
292
295
|
call.reject(error);
|
|
293
296
|
}
|
|
294
297
|
};
|
|
@@ -302,9 +305,12 @@ const pump = (cell: CommandsCell) => {
|
|
|
302
305
|
if (!call) return;
|
|
303
306
|
const handler = cell.handlers[call.method];
|
|
304
307
|
if (handler === undefined) {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
+
const error = new StatewireSendError({
|
|
309
|
+
fate: "unknown-command",
|
|
310
|
+
cause: "server",
|
|
311
|
+
});
|
|
312
|
+
call.origin?.onSettle?.({ type: "rejected", error });
|
|
313
|
+
call.reject(error);
|
|
308
314
|
pump(cell);
|
|
309
315
|
return;
|
|
310
316
|
}
|
|
@@ -314,6 +320,10 @@ const pump = (cell: CommandsCell) => {
|
|
|
314
320
|
ts: call.ts,
|
|
315
321
|
seq: ++cell.seq,
|
|
316
322
|
acked: false,
|
|
323
|
+
...(call.origin !== undefined && { origin: call.origin }),
|
|
324
|
+
...(call.origin?.clientId !== undefined && {
|
|
325
|
+
clientId: call.origin.clientId,
|
|
326
|
+
}),
|
|
317
327
|
};
|
|
318
328
|
cell.running = true;
|
|
319
329
|
cell.listener?.onStart(inv);
|
|
@@ -321,9 +331,9 @@ const pump = (cell: CommandsCell) => {
|
|
|
321
331
|
cell.listener?.onCovered?.(inv);
|
|
322
332
|
void (async () => {
|
|
323
333
|
try {
|
|
324
|
-
const value = await (
|
|
325
|
-
...
|
|
326
|
-
);
|
|
334
|
+
const value = await (
|
|
335
|
+
handler as (...args: readonly unknown[]) => unknown
|
|
336
|
+
).call(call.origin?.caller ?? LOCAL_CALLER, ...call.params);
|
|
327
337
|
const execution =
|
|
328
338
|
typeof value === "object" && value !== null
|
|
329
339
|
? executionStates.get(value)
|
|
@@ -363,17 +373,13 @@ const watchStall = (cell: CommandsCell) => {
|
|
|
363
373
|
}, STALL_WARN_MS);
|
|
364
374
|
};
|
|
365
375
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
export const useStatewireCommands = <THandlers extends StatewireCommands>(
|
|
375
|
-
commands: THandlers | undefined | null | false,
|
|
376
|
-
): StatewireCommandsProxy<StatewireHandledCommands<THandlers>> => {
|
|
376
|
+
export const useStatewireCommands = <THandlers extends Statewire.Commands>(
|
|
377
|
+
commands:
|
|
378
|
+
| (THandlers & ThisType<StatewireHost.Caller>)
|
|
379
|
+
| undefined
|
|
380
|
+
| null
|
|
381
|
+
| false,
|
|
382
|
+
): Statewire.CommandsProxy<StatewireHost.HandledCommands<THandlers>> => {
|
|
377
383
|
const [built] = useState(() => {
|
|
378
384
|
const cell: CommandsCell = {
|
|
379
385
|
handlers: null,
|
|
@@ -384,39 +390,45 @@ export const useStatewireCommands = <THandlers extends StatewireCommands>(
|
|
|
384
390
|
unmounted: false,
|
|
385
391
|
stallTimer: null,
|
|
386
392
|
};
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
if (
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
fn = (...params: unknown[]) =>
|
|
396
|
-
new Promise((resolve, reject) => {
|
|
397
|
-
if (cell.unmounted) {
|
|
398
|
-
reject(new Error("statewire: commands called after unmount"));
|
|
399
|
-
return;
|
|
400
|
-
}
|
|
401
|
-
cell.queue.push({
|
|
402
|
-
method,
|
|
403
|
-
params,
|
|
404
|
-
ts: Date.now(),
|
|
405
|
-
resolve,
|
|
406
|
-
reject,
|
|
407
|
-
});
|
|
408
|
-
pump(cell);
|
|
409
|
-
});
|
|
410
|
-
memo.set(method, fn);
|
|
393
|
+
const proxy = createMethodProxy<
|
|
394
|
+
Statewire.CommandsProxy<StatewireHost.HandledCommands<THandlers>>
|
|
395
|
+
>(
|
|
396
|
+
(method, params) =>
|
|
397
|
+
new Promise((resolve, reject) => {
|
|
398
|
+
if (cell.unmounted) {
|
|
399
|
+
reject(new Error("statewire: commands called after unmount"));
|
|
400
|
+
return;
|
|
411
401
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
402
|
+
cell.queue.push({
|
|
403
|
+
method,
|
|
404
|
+
params,
|
|
405
|
+
ts: Date.now(),
|
|
406
|
+
resolve,
|
|
407
|
+
reject,
|
|
408
|
+
});
|
|
409
|
+
pump(cell);
|
|
410
|
+
}),
|
|
415
411
|
);
|
|
416
412
|
commandsHosts.set(proxy, {
|
|
417
413
|
setListener: (listener) => {
|
|
418
414
|
cell.listener = listener;
|
|
419
415
|
},
|
|
416
|
+
submit: (origin) =>
|
|
417
|
+
new Promise((resolve, reject) => {
|
|
418
|
+
if (cell.unmounted) {
|
|
419
|
+
reject(new Error("statewire: commands called after unmount"));
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
cell.queue.push({
|
|
423
|
+
method: origin.method,
|
|
424
|
+
params: origin.params,
|
|
425
|
+
ts: Date.now(),
|
|
426
|
+
resolve,
|
|
427
|
+
reject,
|
|
428
|
+
origin,
|
|
429
|
+
});
|
|
430
|
+
pump(cell);
|
|
431
|
+
}),
|
|
420
432
|
});
|
|
421
433
|
return { cell, proxy };
|
|
422
434
|
});
|
|
@@ -458,8 +470,8 @@ export type EmitHost = {
|
|
|
458
470
|
|
|
459
471
|
const emitHosts = new WeakMap<object, EmitHost>();
|
|
460
472
|
|
|
461
|
-
export const getEmitHost = (emit: object): EmitHost | undefined =>
|
|
462
|
-
emitHosts.get(emit);
|
|
473
|
+
export const getEmitHost = (emit: object | undefined): EmitHost | undefined =>
|
|
474
|
+
emit && emitHosts.get(emit);
|
|
463
475
|
|
|
464
476
|
export const useStatewireEmit = <TEvent>(): ((event: TEvent) => void) => {
|
|
465
477
|
const [built] = useState(() => {
|
|
@@ -468,7 +480,7 @@ export const useStatewireEmit = <TEvent>(): ((event: TEvent) => void) => {
|
|
|
468
480
|
if (!cell.sink) {
|
|
469
481
|
throw new Error("statewire: emit before a host bound the event sink");
|
|
470
482
|
}
|
|
471
|
-
cell.sink(
|
|
483
|
+
cell.sink(sanitizeValue(event));
|
|
472
484
|
};
|
|
473
485
|
emitHosts.set(emit, {
|
|
474
486
|
setSink: (sink) => {
|