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/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,17 @@ 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
|
+
onAck?: () => void;
|
|
207
|
+
onSettle?: (outcome: CommandsOutcome) => void;
|
|
208
|
+
};
|
|
209
|
+
|
|
223
210
|
export type CommandsHost = {
|
|
224
211
|
setListener: (listener: CommandsListener | null) => void;
|
|
212
|
+
submit?: (call: CommandsSubmission) => Promise<unknown>;
|
|
225
213
|
};
|
|
226
214
|
|
|
227
215
|
const commandsHosts = new WeakMap<object, CommandsHost>();
|
|
@@ -239,9 +227,13 @@ type QueuedCall = {
|
|
|
239
227
|
ts: number;
|
|
240
228
|
resolve: (value: unknown) => void;
|
|
241
229
|
reject: (error: unknown) => void;
|
|
230
|
+
origin?: CommandsSubmission;
|
|
242
231
|
};
|
|
243
232
|
|
|
244
|
-
type ActiveInvocation = CommandsInvocation & {
|
|
233
|
+
type ActiveInvocation = CommandsInvocation & {
|
|
234
|
+
acked: boolean;
|
|
235
|
+
origin?: CommandsSubmission;
|
|
236
|
+
};
|
|
245
237
|
|
|
246
238
|
type CommandsCell = {
|
|
247
239
|
handlers: Record<string, (...args: never[]) => unknown> | null;
|
|
@@ -270,6 +262,7 @@ const ackInvocation = (cell: CommandsCell, inv: ActiveInvocation) => {
|
|
|
270
262
|
if (inv.acked) return;
|
|
271
263
|
inv.acked = true;
|
|
272
264
|
cell.listener?.onAck(inv);
|
|
265
|
+
inv.origin?.onAck?.();
|
|
273
266
|
};
|
|
274
267
|
|
|
275
268
|
const settleInvocation = (
|
|
@@ -283,12 +276,15 @@ const settleInvocation = (
|
|
|
283
276
|
const outcome: CommandsOutcome =
|
|
284
277
|
settled.value === undefined
|
|
285
278
|
? { type: "accepted" }
|
|
286
|
-
: { type: "accepted", payload:
|
|
279
|
+
: { type: "accepted", payload: sanitizeValue(settled.value) };
|
|
287
280
|
cell.listener?.onSettle(inv, outcome);
|
|
281
|
+
inv.origin?.onSettle?.(outcome);
|
|
288
282
|
call.resolve(outcome.payload);
|
|
289
283
|
} else {
|
|
290
284
|
const error = toSendError(settled.error);
|
|
291
|
-
|
|
285
|
+
const outcome: CommandsOutcome = { type: "rejected", error };
|
|
286
|
+
cell.listener?.onSettle(inv, outcome);
|
|
287
|
+
inv.origin?.onSettle?.(outcome);
|
|
292
288
|
call.reject(error);
|
|
293
289
|
}
|
|
294
290
|
};
|
|
@@ -302,9 +298,12 @@ const pump = (cell: CommandsCell) => {
|
|
|
302
298
|
if (!call) return;
|
|
303
299
|
const handler = cell.handlers[call.method];
|
|
304
300
|
if (handler === undefined) {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
301
|
+
const error = new StatewireSendError({
|
|
302
|
+
fate: "unknown-command",
|
|
303
|
+
cause: "server",
|
|
304
|
+
});
|
|
305
|
+
call.origin?.onSettle?.({ type: "rejected", error });
|
|
306
|
+
call.reject(error);
|
|
308
307
|
pump(cell);
|
|
309
308
|
return;
|
|
310
309
|
}
|
|
@@ -314,6 +313,10 @@ const pump = (cell: CommandsCell) => {
|
|
|
314
313
|
ts: call.ts,
|
|
315
314
|
seq: ++cell.seq,
|
|
316
315
|
acked: false,
|
|
316
|
+
...(call.origin !== undefined && { origin: call.origin }),
|
|
317
|
+
...(call.origin?.clientId !== undefined && {
|
|
318
|
+
clientId: call.origin.clientId,
|
|
319
|
+
}),
|
|
317
320
|
};
|
|
318
321
|
cell.running = true;
|
|
319
322
|
cell.listener?.onStart(inv);
|
|
@@ -363,17 +366,9 @@ const watchStall = (cell: CommandsCell) => {
|
|
|
363
366
|
}, STALL_WARN_MS);
|
|
364
367
|
};
|
|
365
368
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
export type StatewireHandledCommands<THandlers extends StatewireCommands> = {
|
|
369
|
-
[K in keyof THandlers]: (
|
|
370
|
-
...args: Parameters<THandlers[K]>
|
|
371
|
-
) => UnwrapExecution<Awaited<ReturnType<THandlers[K]>>>;
|
|
372
|
-
};
|
|
373
|
-
|
|
374
|
-
export const useStatewireCommands = <THandlers extends StatewireCommands>(
|
|
369
|
+
export const useStatewireCommands = <THandlers extends Statewire.Commands>(
|
|
375
370
|
commands: THandlers | undefined | null | false,
|
|
376
|
-
):
|
|
371
|
+
): Statewire.CommandsProxy<StatewireHost.HandledCommands<THandlers>> => {
|
|
377
372
|
const [built] = useState(() => {
|
|
378
373
|
const cell: CommandsCell = {
|
|
379
374
|
handlers: null,
|
|
@@ -384,39 +379,45 @@ export const useStatewireCommands = <THandlers extends StatewireCommands>(
|
|
|
384
379
|
unmounted: false,
|
|
385
380
|
stallTimer: null,
|
|
386
381
|
};
|
|
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);
|
|
382
|
+
const proxy = createMethodProxy<
|
|
383
|
+
Statewire.CommandsProxy<StatewireHost.HandledCommands<THandlers>>
|
|
384
|
+
>(
|
|
385
|
+
(method, params) =>
|
|
386
|
+
new Promise((resolve, reject) => {
|
|
387
|
+
if (cell.unmounted) {
|
|
388
|
+
reject(new Error("statewire: commands called after unmount"));
|
|
389
|
+
return;
|
|
411
390
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
391
|
+
cell.queue.push({
|
|
392
|
+
method,
|
|
393
|
+
params,
|
|
394
|
+
ts: Date.now(),
|
|
395
|
+
resolve,
|
|
396
|
+
reject,
|
|
397
|
+
});
|
|
398
|
+
pump(cell);
|
|
399
|
+
}),
|
|
415
400
|
);
|
|
416
401
|
commandsHosts.set(proxy, {
|
|
417
402
|
setListener: (listener) => {
|
|
418
403
|
cell.listener = listener;
|
|
419
404
|
},
|
|
405
|
+
submit: (origin) =>
|
|
406
|
+
new Promise((resolve, reject) => {
|
|
407
|
+
if (cell.unmounted) {
|
|
408
|
+
reject(new Error("statewire: commands called after unmount"));
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
cell.queue.push({
|
|
412
|
+
method: origin.method,
|
|
413
|
+
params: origin.params,
|
|
414
|
+
ts: Date.now(),
|
|
415
|
+
resolve,
|
|
416
|
+
reject,
|
|
417
|
+
origin,
|
|
418
|
+
});
|
|
419
|
+
pump(cell);
|
|
420
|
+
}),
|
|
420
421
|
});
|
|
421
422
|
return { cell, proxy };
|
|
422
423
|
});
|
|
@@ -458,8 +459,8 @@ export type EmitHost = {
|
|
|
458
459
|
|
|
459
460
|
const emitHosts = new WeakMap<object, EmitHost>();
|
|
460
461
|
|
|
461
|
-
export const getEmitHost = (emit: object): EmitHost | undefined =>
|
|
462
|
-
emitHosts.get(emit);
|
|
462
|
+
export const getEmitHost = (emit: object | undefined): EmitHost | undefined =>
|
|
463
|
+
emit && emitHosts.get(emit);
|
|
463
464
|
|
|
464
465
|
export const useStatewireEmit = <TEvent>(): ((event: TEvent) => void) => {
|
|
465
466
|
const [built] = useState(() => {
|
|
@@ -468,7 +469,7 @@ export const useStatewireEmit = <TEvent>(): ((event: TEvent) => void) => {
|
|
|
468
469
|
if (!cell.sink) {
|
|
469
470
|
throw new Error("statewire: emit before a host bound the event sink");
|
|
470
471
|
}
|
|
471
|
-
cell.sink(
|
|
472
|
+
cell.sink(sanitizeValue(event));
|
|
472
473
|
};
|
|
473
474
|
emitHosts.set(emit, {
|
|
474
475
|
setSink: (sink) => {
|