statewire 0.12.2 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/core.d.ts +2 -0
- package/dist/client/core.d.ts.map +1 -1
- package/dist/client/core.js +21 -3
- package/dist/client/core.js.map +1 -1
- package/dist/client/session.d.ts +4 -1
- package/dist/client/session.d.ts.map +1 -1
- package/dist/client/session.js +60 -2
- package/dist/client/session.js.map +1 -1
- package/dist/statewire.d.ts +22 -0
- package/dist/statewire.d.ts.map +1 -1
- package/dist/statewire.js +31 -11
- package/dist/statewire.js.map +1 -1
- package/dist/transports/remote.d.ts.map +1 -1
- package/dist/transports/remote.js +7 -2
- package/dist/transports/remote.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/src/client/core.ts +31 -3
- package/src/client/session.ts +100 -0
- package/src/statewire.ts +55 -5
- package/src/transports/remote.ts +8 -0
- package/src/types.ts +6 -0
package/src/statewire.ts
CHANGED
|
@@ -59,10 +59,21 @@ export const useStatewire = <
|
|
|
59
59
|
core.getState,
|
|
60
60
|
) as TState;
|
|
61
61
|
const isBusy = useSyncExternalStore(subscribe, core.getBusy, core.getBusy);
|
|
62
|
+
const failed = useSyncExternalStore(
|
|
63
|
+
subscribe,
|
|
64
|
+
core.getFailed,
|
|
65
|
+
core.getFailed,
|
|
66
|
+
);
|
|
62
67
|
const connection = value.connection ?? LIVE_CONNECTION;
|
|
63
68
|
|
|
64
|
-
return useMemo(
|
|
65
|
-
() =>
|
|
69
|
+
return useMemo(() => {
|
|
70
|
+
const failures = (): NonNullable<typeof value.failures> => {
|
|
71
|
+
if (!value.failures) {
|
|
72
|
+
throw new Error("statewire: this transport has no failed-send lane");
|
|
73
|
+
}
|
|
74
|
+
return value.failures;
|
|
75
|
+
};
|
|
76
|
+
return {
|
|
66
77
|
state,
|
|
67
78
|
getState: core.getState as () => TState,
|
|
68
79
|
connection,
|
|
@@ -76,9 +87,13 @@ export const useStatewire = <
|
|
|
76
87
|
}
|
|
77
88
|
value.setContext(contextValue);
|
|
78
89
|
},
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
90
|
+
failed,
|
|
91
|
+
retryFailed: (entry: StatewireClient.FailedCommand) =>
|
|
92
|
+
failures().retry(entry),
|
|
93
|
+
discardFailed: (entry: StatewireClient.FailedCommand) =>
|
|
94
|
+
failures().discard(entry),
|
|
95
|
+
};
|
|
96
|
+
}, [state, isBusy, failed, connection, core, value]);
|
|
82
97
|
};
|
|
83
98
|
|
|
84
99
|
export const StatewireResource = <
|
|
@@ -121,6 +136,18 @@ export class StatewireClient<
|
|
|
121
136
|
this.root.getValue().setContext(value);
|
|
122
137
|
}
|
|
123
138
|
|
|
139
|
+
get failed(): readonly StatewireClient.FailedCommand[] {
|
|
140
|
+
return this.root.getValue().failed;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
retryFailed(entry: StatewireClient.FailedCommand): Promise<unknown> {
|
|
144
|
+
return this.root.getValue().retryFailed(entry);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
discardFailed(entry: StatewireClient.FailedCommand): void {
|
|
148
|
+
this.root.getValue().discardFailed(entry);
|
|
149
|
+
}
|
|
150
|
+
|
|
124
151
|
subscribe(listener: () => void): () => void {
|
|
125
152
|
return this.root.subscribe(listener);
|
|
126
153
|
}
|
|
@@ -155,6 +182,11 @@ export namespace StatewireClient {
|
|
|
155
182
|
ctx: { state: NonNullable<TState> },
|
|
156
183
|
) => void;
|
|
157
184
|
onConnectionChange?: (connection: Connection) => void;
|
|
185
|
+
/** Drops a retained failed entry once a canonical snapshot shows its effect landed; checked against each new snapshot. */
|
|
186
|
+
failureCovered?: (
|
|
187
|
+
state: NonNullable<TState>,
|
|
188
|
+
entry: FailedCommand,
|
|
189
|
+
) => boolean;
|
|
158
190
|
};
|
|
159
191
|
|
|
160
192
|
export type Helpers<
|
|
@@ -171,6 +203,12 @@ export namespace StatewireClient {
|
|
|
171
203
|
isBusy: boolean;
|
|
172
204
|
/** Replace this client's ambient context on the server; throws on transports without a client context channel. */
|
|
173
205
|
setContext: (value: Record<string, unknown>) => void;
|
|
206
|
+
/** Non-accepted sends retained in the session record; reload with it. */
|
|
207
|
+
failed: readonly FailedCommand[];
|
|
208
|
+
/** Re-enqueues the entry's command on fresh clocks and removes the entry. */
|
|
209
|
+
retryFailed: (entry: FailedCommand) => Promise<unknown>;
|
|
210
|
+
/** Removes the entry. */
|
|
211
|
+
discardFailed: (entry: FailedCommand) => void;
|
|
174
212
|
};
|
|
175
213
|
|
|
176
214
|
export type ConnectionState =
|
|
@@ -259,6 +297,16 @@ export namespace StatewireClient {
|
|
|
259
297
|
}>,
|
|
260
298
|
) => "requeue" | "drop";
|
|
261
299
|
|
|
300
|
+
/** A non-accepted send retained for the user: the wire statement, its failure, and its original clocks. */
|
|
301
|
+
export type FailedCommand = {
|
|
302
|
+
readonly command: string;
|
|
303
|
+
readonly params: readonly unknown[];
|
|
304
|
+
readonly meta?: unknown;
|
|
305
|
+
readonly failure: SendFailure;
|
|
306
|
+
readonly ts: number;
|
|
307
|
+
readonly seq: number;
|
|
308
|
+
};
|
|
309
|
+
|
|
262
310
|
/** A command as persisted while un-durable: its lane seq plus the wire statement. */
|
|
263
311
|
export type SessionCommand = {
|
|
264
312
|
seq: number;
|
|
@@ -275,6 +323,8 @@ export namespace StatewireClient {
|
|
|
275
323
|
commands: readonly SessionCommand[];
|
|
276
324
|
/** Terminal verdicts parked for the sharer that minted the seq (shared storage only). */
|
|
277
325
|
verdicts?: readonly SessionVerdict[];
|
|
326
|
+
/** Non-accepted sends retained for the user; survive record rebases and resets. */
|
|
327
|
+
failed?: readonly FailedCommand[];
|
|
278
328
|
};
|
|
279
329
|
|
|
280
330
|
/** A terminal verdict one sharer's connection carried, parked for the sharer awaiting the seq. */
|
package/src/transports/remote.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { useEffect } from "react";
|
|
2
2
|
import type { Statewire } from "../types";
|
|
3
3
|
import {
|
|
4
|
+
discardFailed,
|
|
5
|
+
failedCommands,
|
|
6
|
+
retryFailed,
|
|
4
7
|
setContext,
|
|
5
8
|
setTransport,
|
|
6
9
|
useStatewireSession,
|
|
@@ -39,5 +42,10 @@ export const useStatewireRemote = <
|
|
|
39
42
|
return !s.idle;
|
|
40
43
|
},
|
|
41
44
|
setContext: (value) => setContext(s, value),
|
|
45
|
+
failures: {
|
|
46
|
+
list: () => failedCommands(s),
|
|
47
|
+
retry: (entry) => retryFailed(s, entry),
|
|
48
|
+
discard: (entry) => discardFailed(s, entry),
|
|
49
|
+
},
|
|
42
50
|
};
|
|
43
51
|
};
|
package/src/types.ts
CHANGED
|
@@ -59,6 +59,12 @@ export interface Statewire<
|
|
|
59
59
|
isBusy?: boolean;
|
|
60
60
|
/** Replace this client's ambient context on the server; it rides the ctx lane (no verdict). */
|
|
61
61
|
setContext?: (value: Record<string, unknown>) => void;
|
|
62
|
+
/** The retained failed-send lane; present on transports with a durable session. */
|
|
63
|
+
failures?: {
|
|
64
|
+
list: () => readonly StatewireClient.FailedCommand[];
|
|
65
|
+
retry: (entry: StatewireClient.FailedCommand) => Promise<unknown>;
|
|
66
|
+
discard: (entry: StatewireClient.FailedCommand) => void;
|
|
67
|
+
};
|
|
62
68
|
}
|
|
63
69
|
|
|
64
70
|
const describe = (failure: StatewireClient.SendFailure): string => {
|