@alexkroman1/aai-ui 0.14.1 → 0.14.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/dist/_actor-store.d.ts +16 -0
- package/dist/_recover-run.d.ts +13 -17
- package/dist/_submission-state.d.ts +33 -69
- package/dist/_tap-to-talk-state.d.ts +2 -6
- package/dist/_workflow-form-state.d.ts +117 -0
- package/dist/default-client/assets/{audio-CABEDpag.js → audio-C_FqJApT.js} +1 -1
- package/dist/default-client/assets/{capture-processor-X_vJUkv4.js → capture-processor-C4rOgMnr.js} +1 -1
- package/dist/default-client/assets/{index-DdRGAZxZ.js → index-CRMh5gpc.js} +11 -11
- package/dist/default-client/assets/{playback-processor-DdO-2G6m.js → playback-processor-J74Xm_Cx.js} +1 -1
- package/dist/default-client/index.html +1 -1
- package/dist/define-client.js +1 -1
- package/dist/inbox.d.ts +2 -1
- package/dist/index.js +477 -316
- package/dist/session/connection.d.ts +121 -0
- package/dist/session/handshake.d.ts +20 -33
- package/dist/session/index.d.ts +1 -1
- package/dist/session/index.js +1 -1
- package/dist/session/messages.d.ts +6 -0
- package/dist/session/reconnect.d.ts +5 -0
- package/dist/session/types.d.ts +3 -13
- package/dist/{session-6tIQSoak.js → session-CYdjRvbl.js} +277 -147
- package/dist/upload/index.d.ts +2 -2
- package/package.json +2 -2
- package/dist/upload/pause.d.ts +0 -31
|
@@ -646,6 +646,218 @@ function closeFailure(event, socketErrored) {
|
|
|
646
646
|
return socketErrored ? "WebSocket connection error" : null;
|
|
647
647
|
}
|
|
648
648
|
//#endregion
|
|
649
|
+
//#region src/session/handshake.ts
|
|
650
|
+
/** What the session reports once the budget below is spent. */
|
|
651
|
+
const HANDSHAKE_ERROR = {
|
|
652
|
+
code: "connection",
|
|
653
|
+
message: "Agent did not complete the session handshake",
|
|
654
|
+
fatal: false
|
|
655
|
+
};
|
|
656
|
+
//#endregion
|
|
657
|
+
//#region src/session/connection.ts
|
|
658
|
+
/**
|
|
659
|
+
* One browser session's CONNECTION — the socket, the handshake deadline over
|
|
660
|
+
* it, and the server's idle retirement — as a statechart.
|
|
661
|
+
*
|
|
662
|
+
* `link` is `closed → open`, and `open` is `dialing → awaitingHandshake → live`:
|
|
663
|
+
*
|
|
664
|
+
* - **The socket belongs to `open`.** Its entry dials and attaches the
|
|
665
|
+
* listeners; its exit is the ONE teardown (detach, release the pre-connect
|
|
666
|
+
* mic, tear the audio path down, close the socket). A hang-up, a terminal
|
|
667
|
+
* close, an exhausted handshake and a fresh `connect()` all leave `open`, so
|
|
668
|
+
* none of them restates it, and exit actions run before the transition's, so
|
|
669
|
+
* the teardown always precedes the snapshot write reporting the end.
|
|
670
|
+
* - **The handshake deadline is an `after` on `awaitingHandshake`** (see
|
|
671
|
+
* `session/handshake.ts` for why there is one). Leaving the state disarms it,
|
|
672
|
+
* so a `config` frame, a close or a teardown cannot leave it armed against
|
|
673
|
+
* the next attempt — there is no timer to clear by hand on abort.
|
|
674
|
+
* - **partysocket owns the reconnect backoff.** A close that partysocket will
|
|
675
|
+
* retry goes back to `dialing` and waits for its next `open`; this machine
|
|
676
|
+
* never schedules an attempt. The deadline forces one only for a socket
|
|
677
|
+
* partysocket thinks is healthy (`forceReconnect`).
|
|
678
|
+
*
|
|
679
|
+
* `retired` is a second region because it outlives a socket: the server's
|
|
680
|
+
* `session.timedOut` marks the close that follows as EXPECTED, so it is not
|
|
681
|
+
* retried. Without it the automatic reconnect would re-open the session the
|
|
682
|
+
* server just reclaimed — a tab left open would cycle forever and the guest
|
|
683
|
+
* would never see zero sessions, which is the point of the timeout. Only a
|
|
684
|
+
* fresh `connect()` (the user asking again) clears it.
|
|
685
|
+
*
|
|
686
|
+
* The machine owns WHEN; every HOW — the socket, the snapshot, the audio path —
|
|
687
|
+
* is an injected {@link ConnectionEffects} call, as in `session/audio-state.ts`.
|
|
688
|
+
*/
|
|
689
|
+
const connectionMachine = setup({
|
|
690
|
+
types: {},
|
|
691
|
+
delays: { HANDSHAKE_TIMEOUT: 1e4 },
|
|
692
|
+
guards: {
|
|
693
|
+
/**
|
|
694
|
+
* partysocket will retry this close, and nothing says it must not: a FATAL
|
|
695
|
+
* error is not retryable by construction, nor is an idle retirement.
|
|
696
|
+
*/
|
|
697
|
+
mayReconnect: and([({ context, event }) => event.type === "DROPPED" && event.reconnecting && !context.effects.fatal(), not(stateIn({ retired: "yes" }))]),
|
|
698
|
+
/** Budget left, and a socket that can re-dial at all. */
|
|
699
|
+
mayRedial: ({ context }) => context.timeouts + 1 < 3 && context.effects.canRedial()
|
|
700
|
+
},
|
|
701
|
+
actions: {
|
|
702
|
+
spend: assign({ timeouts: ({ context }) => context.timeouts + 1 }),
|
|
703
|
+
/** A socket error inside a retry cycle is not terminal. */
|
|
704
|
+
forgetError: assign({ errored: false }),
|
|
705
|
+
retrying: ({ context }) => context.effects.retrying(),
|
|
706
|
+
opened: ({ context }) => context.effects.opened(),
|
|
707
|
+
dial: ({ context, event }) => context.link.dial(event.type === "DIAL" ? event.signal : void 0),
|
|
708
|
+
/** The ONE teardown: every way out of `open` runs it, before anything is reported. */
|
|
709
|
+
teardown: ({ context }) => context.link.teardown(),
|
|
710
|
+
reportClose: ({ context, event }) => {
|
|
711
|
+
if (event.type === "DROPPED") context.effects.closed(closeFailure(event.event, context.errored));
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}).createMachine({
|
|
715
|
+
id: "connection",
|
|
716
|
+
type: "parallel",
|
|
717
|
+
context: ({ input }) => ({
|
|
718
|
+
...input,
|
|
719
|
+
timeouts: 0,
|
|
720
|
+
errored: false
|
|
721
|
+
}),
|
|
722
|
+
states: {
|
|
723
|
+
link: {
|
|
724
|
+
initial: "closed",
|
|
725
|
+
on: {
|
|
726
|
+
DIAL: {
|
|
727
|
+
target: ".open",
|
|
728
|
+
reenter: true
|
|
729
|
+
},
|
|
730
|
+
HANG_UP: ".closed"
|
|
731
|
+
},
|
|
732
|
+
states: {
|
|
733
|
+
closed: {},
|
|
734
|
+
open: {
|
|
735
|
+
entry: [assign({
|
|
736
|
+
timeouts: 0,
|
|
737
|
+
errored: false
|
|
738
|
+
}), "dial"],
|
|
739
|
+
exit: "teardown",
|
|
740
|
+
initial: "dialing",
|
|
741
|
+
on: {
|
|
742
|
+
ERRORED: { actions: assign({ errored: true }) },
|
|
743
|
+
CONFIGURED: {
|
|
744
|
+
target: ".live",
|
|
745
|
+
actions: [assign({ timeouts: 0 }), ({ context, event }) => context.effects.configured(event.config)]
|
|
746
|
+
},
|
|
747
|
+
DROPPED: [{
|
|
748
|
+
guard: "mayReconnect",
|
|
749
|
+
target: ".dialing",
|
|
750
|
+
actions: ["forgetError", "retrying"]
|
|
751
|
+
}, {
|
|
752
|
+
target: "closed",
|
|
753
|
+
actions: "reportClose"
|
|
754
|
+
}]
|
|
755
|
+
},
|
|
756
|
+
states: {
|
|
757
|
+
dialing: { on: { OPENED: {
|
|
758
|
+
target: "awaitingHandshake",
|
|
759
|
+
actions: "opened"
|
|
760
|
+
} } },
|
|
761
|
+
awaitingHandshake: {
|
|
762
|
+
on: { OPENED: {
|
|
763
|
+
target: "awaitingHandshake",
|
|
764
|
+
reenter: true,
|
|
765
|
+
actions: "opened"
|
|
766
|
+
} },
|
|
767
|
+
after: { HANDSHAKE_TIMEOUT: [{
|
|
768
|
+
guard: "mayRedial",
|
|
769
|
+
target: "dialing",
|
|
770
|
+
actions: [
|
|
771
|
+
"spend",
|
|
772
|
+
({ context }) => context.effects.redial(),
|
|
773
|
+
"retrying"
|
|
774
|
+
]
|
|
775
|
+
}, {
|
|
776
|
+
target: "#connection.link.closed",
|
|
777
|
+
actions: ({ context }) => context.effects.exhausted()
|
|
778
|
+
}] }
|
|
779
|
+
},
|
|
780
|
+
live: { on: { OPENED: {
|
|
781
|
+
target: "awaitingHandshake",
|
|
782
|
+
actions: "opened"
|
|
783
|
+
} } }
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
},
|
|
788
|
+
retired: {
|
|
789
|
+
initial: "no",
|
|
790
|
+
states: {
|
|
791
|
+
no: { on: { RETIRED: "yes" } },
|
|
792
|
+
yes: { on: { DIAL: "no" } }
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
});
|
|
797
|
+
/** The socket half of {@link Link}: listeners in, teardown out. */
|
|
798
|
+
function createLink(effects, send) {
|
|
799
|
+
let current = null;
|
|
800
|
+
return {
|
|
801
|
+
dial(caller) {
|
|
802
|
+
const controller = new AbortController();
|
|
803
|
+
const { signal } = controller;
|
|
804
|
+
caller?.addEventListener("abort", () => effects.abandon(), { signal });
|
|
805
|
+
const socket = effects.dial();
|
|
806
|
+
current = {
|
|
807
|
+
socket,
|
|
808
|
+
controller
|
|
809
|
+
};
|
|
810
|
+
socket.addEventListener("open", () => send({ type: "OPENED" }), { signal });
|
|
811
|
+
socket.addEventListener("message", (event) => {
|
|
812
|
+
const config = effects.receive(event.data);
|
|
813
|
+
if (config) send({
|
|
814
|
+
type: "CONFIGURED",
|
|
815
|
+
config
|
|
816
|
+
});
|
|
817
|
+
}, { signal });
|
|
818
|
+
socket.addEventListener("error", () => send({ type: "ERRORED" }), { signal });
|
|
819
|
+
socket.addEventListener("close", (event) => send({
|
|
820
|
+
type: "DROPPED",
|
|
821
|
+
event,
|
|
822
|
+
reconnecting: effects.retryPending(socket)
|
|
823
|
+
}), { signal });
|
|
824
|
+
},
|
|
825
|
+
teardown() {
|
|
826
|
+
if (current === null) return;
|
|
827
|
+
const { socket, controller } = current;
|
|
828
|
+
current = null;
|
|
829
|
+
controller.abort();
|
|
830
|
+
effects.release(socket);
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
/** Create the connection machine for one browser session. */
|
|
835
|
+
function createConnection(effects) {
|
|
836
|
+
const link = createLink(effects, (event) => self.send(event));
|
|
837
|
+
const self = createActor(connectionMachine, { input: {
|
|
838
|
+
effects,
|
|
839
|
+
link
|
|
840
|
+
} }).start();
|
|
841
|
+
return {
|
|
842
|
+
dial: (signal) => {
|
|
843
|
+
self.send({
|
|
844
|
+
type: "DIAL",
|
|
845
|
+
signal
|
|
846
|
+
});
|
|
847
|
+
},
|
|
848
|
+
hangUp: () => {
|
|
849
|
+
self.send({ type: "HANG_UP" });
|
|
850
|
+
},
|
|
851
|
+
retire: () => {
|
|
852
|
+
self.send({ type: "RETIRED" });
|
|
853
|
+
},
|
|
854
|
+
phase: () => {
|
|
855
|
+
const at = self.getSnapshot().value.link;
|
|
856
|
+
return typeof at === "string" ? "closed" : at.open;
|
|
857
|
+
}
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
//#endregion
|
|
649
861
|
//#region src/session/reconnect.ts
|
|
650
862
|
/**
|
|
651
863
|
* Automatic reconnection for the browser session socket, built on
|
|
@@ -676,6 +888,13 @@ function openReconnectingSocket(urlProvider, protocolsProvider) {
|
|
|
676
888
|
return new ReconnectingWebSocket(urlProvider, protocolsProvider, RECONNECT_OPTIONS);
|
|
677
889
|
}
|
|
678
890
|
/**
|
|
891
|
+
* Does `socket` have reconnect machinery? False for an injected
|
|
892
|
+
* `options.WebSocket` (tests), which a failure then ends for good.
|
|
893
|
+
*/
|
|
894
|
+
function canReconnect(socket) {
|
|
895
|
+
return socket instanceof ReconnectingWebSocket;
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
679
898
|
* Force a fresh connection attempt on a reconnecting socket, reporting
|
|
680
899
|
* whether it was one. Used for a failure partysocket cannot see: a socket it
|
|
681
900
|
* considers open and healthy, whose peer never completed OUR handshake.
|
|
@@ -1018,67 +1237,6 @@ function createDialer(options, onSessionId) {
|
|
|
1018
1237
|
};
|
|
1019
1238
|
}
|
|
1020
1239
|
//#endregion
|
|
1021
|
-
//#region src/session/handshake.ts
|
|
1022
|
-
/** What the session reports once the budget below is spent. */
|
|
1023
|
-
const HANDSHAKE_ERROR = {
|
|
1024
|
-
code: "connection",
|
|
1025
|
-
message: "Agent did not complete the session handshake",
|
|
1026
|
-
fatal: false
|
|
1027
|
-
};
|
|
1028
|
-
/** How long an OPEN socket may go without a `config` frame. */
|
|
1029
|
-
const HANDSHAKE_TIMEOUT_MS = 1e4;
|
|
1030
|
-
/**
|
|
1031
|
-
* How many consecutive handshake timeouts to ride out before giving up.
|
|
1032
|
-
*
|
|
1033
|
-
* `forceReconnect` restarts partysocket's own retry budget, so this is the
|
|
1034
|
-
* budget for this failure mode — without one, a permanently wedged peer would
|
|
1035
|
-
* be re-dialed every ~10s forever, which is the unbounded retry loop
|
|
1036
|
-
* `RECONNECT_OPTIONS.maxRetries` exists to prevent.
|
|
1037
|
-
*
|
|
1038
|
-
* CONSECUTIVE is the whole of it, and only `succeeded()` says so — see its doc.
|
|
1039
|
-
*/
|
|
1040
|
-
const MAX_HANDSHAKE_TIMEOUTS = 3;
|
|
1041
|
-
/**
|
|
1042
|
-
* Watch one connection's handshake.
|
|
1043
|
-
*
|
|
1044
|
-
* `onRetry` fires when a timed-out attempt has been re-dialed (the sandbox
|
|
1045
|
-
* behind the endpoint may have been replaced, and the URL provider re-brokers
|
|
1046
|
-
* on the next attempt); `onExhausted` when the budget is spent, or when the
|
|
1047
|
-
* socket has no reconnect machinery at all (an injected `options.WebSocket`).
|
|
1048
|
-
*
|
|
1049
|
-
* @internal
|
|
1050
|
-
*/
|
|
1051
|
-
function createHandshakeGuard(opts) {
|
|
1052
|
-
let timer;
|
|
1053
|
-
let timeouts = 0;
|
|
1054
|
-
function disarm() {
|
|
1055
|
-
if (timer === void 0) return;
|
|
1056
|
-
clearTimeout(timer);
|
|
1057
|
-
timer = void 0;
|
|
1058
|
-
}
|
|
1059
|
-
function fire() {
|
|
1060
|
-
timer = void 0;
|
|
1061
|
-
timeouts += 1;
|
|
1062
|
-
if (timeouts < MAX_HANDSHAKE_TIMEOUTS && forceReconnect(opts.socket)) {
|
|
1063
|
-
opts.onRetry();
|
|
1064
|
-
return;
|
|
1065
|
-
}
|
|
1066
|
-
opts.onExhausted();
|
|
1067
|
-
}
|
|
1068
|
-
opts.signal.addEventListener("abort", disarm);
|
|
1069
|
-
return {
|
|
1070
|
-
arm() {
|
|
1071
|
-
disarm();
|
|
1072
|
-
timer = setTimeout(fire, HANDSHAKE_TIMEOUT_MS);
|
|
1073
|
-
},
|
|
1074
|
-
disarm,
|
|
1075
|
-
succeeded() {
|
|
1076
|
-
disarm();
|
|
1077
|
-
timeouts = 0;
|
|
1078
|
-
}
|
|
1079
|
-
};
|
|
1080
|
-
}
|
|
1081
|
-
//#endregion
|
|
1082
1240
|
//#region src/session/messages.ts
|
|
1083
1241
|
/**
|
|
1084
1242
|
* Incoming-message handling for the voice session core.
|
|
@@ -1124,7 +1282,7 @@ function appendCapped(list, item, cap) {
|
|
|
1124
1282
|
* `ConnState.turn`).
|
|
1125
1283
|
*/
|
|
1126
1284
|
function createMessageHandlers(deps) {
|
|
1127
|
-
const { getSnapshot, updateState, conn, agentState, audio } = deps;
|
|
1285
|
+
const { getSnapshot, updateState, conn, agentState, audio, retire } = deps;
|
|
1128
1286
|
/** Monotonically increasing counter for custom events -- used by useEvent to deduplicate. */
|
|
1129
1287
|
let customEventSeq = 0;
|
|
1130
1288
|
/** Monotonically increasing counter for chat messages -- stable render keys
|
|
@@ -1332,7 +1490,7 @@ function createMessageHandlers(deps) {
|
|
|
1332
1490
|
case "error.reported":
|
|
1333
1491
|
handleErrorEvent(e);
|
|
1334
1492
|
break;
|
|
1335
|
-
case "session.timedOut":
|
|
1493
|
+
case "session.timedOut": retire();
|
|
1336
1494
|
}
|
|
1337
1495
|
}
|
|
1338
1496
|
/** Enqueue a PCM16 audio chunk for playback. Transitions state to `"speaking"` on the first chunk. */
|
|
@@ -1895,10 +2053,8 @@ function createBrowserSessionWith(options, internals) {
|
|
|
1895
2053
|
const agentState = createSessionStateMachine();
|
|
1896
2054
|
const conn = {
|
|
1897
2055
|
ws: null,
|
|
1898
|
-
retiredByServer: false,
|
|
1899
2056
|
turn: createEpoch()
|
|
1900
2057
|
};
|
|
1901
|
-
let connectionController = null;
|
|
1902
2058
|
const identity = createSessionIdentity(options, () => dialer);
|
|
1903
2059
|
const dialer = createDialer({
|
|
1904
2060
|
...options,
|
|
@@ -1941,17 +2097,9 @@ function createBrowserSessionWith(options, internals) {
|
|
|
1941
2097
|
updateState,
|
|
1942
2098
|
conn,
|
|
1943
2099
|
agentState,
|
|
1944
|
-
audio
|
|
2100
|
+
audio,
|
|
2101
|
+
retire: () => connection.retire()
|
|
1945
2102
|
});
|
|
1946
|
-
/** Abort the in-flight connection and release audio + WebSocket resources. */
|
|
1947
|
-
function teardownConnection() {
|
|
1948
|
-
connectionController?.abort();
|
|
1949
|
-
connectionController = null;
|
|
1950
|
-
preConnect.release();
|
|
1951
|
-
audio.teardown();
|
|
1952
|
-
conn.ws?.close();
|
|
1953
|
-
conn.ws = null;
|
|
1954
|
-
}
|
|
1955
2103
|
/**
|
|
1956
2104
|
* React to the server's `session.configured` frame: record it and set up the
|
|
1957
2105
|
* session's audio path. **It no longer replays history, and that is the
|
|
@@ -1966,78 +2114,43 @@ function createBrowserSessionWith(options, internals) {
|
|
|
1966
2114
|
flushQueued();
|
|
1967
2115
|
audio.start(config);
|
|
1968
2116
|
}
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
socket.binaryType = "arraybuffer";
|
|
1984
|
-
conn.ws = socket;
|
|
1985
|
-
let socketErrored = false;
|
|
1986
|
-
const handshake = createHandshakeGuard({
|
|
1987
|
-
socket,
|
|
1988
|
-
signal: sig,
|
|
1989
|
-
onRetry: () => {
|
|
1990
|
-
audio.teardown();
|
|
1991
|
-
updateState({
|
|
1992
|
-
...agentState.apply({ type: "CONNECT" }),
|
|
1993
|
-
recording: false
|
|
1994
|
-
});
|
|
1995
|
-
},
|
|
1996
|
-
onExhausted: () => {
|
|
1997
|
-
preConnect.release();
|
|
1998
|
-
audio.teardown();
|
|
1999
|
-
controller.abort();
|
|
2000
|
-
socket.close();
|
|
2001
|
-
conn.ws = null;
|
|
2002
|
-
updateState({
|
|
2003
|
-
...agentState.apply({
|
|
2004
|
-
type: "FAILED",
|
|
2005
|
-
error: HANDSHAKE_ERROR
|
|
2006
|
-
}),
|
|
2007
|
-
...STOPPED
|
|
2008
|
-
});
|
|
2009
|
-
}
|
|
2010
|
-
});
|
|
2011
|
-
socket.addEventListener("open", () => {
|
|
2012
|
-
updateState(agentState.apply({ type: "SOCKET_OPEN" }));
|
|
2013
|
-
handshake.arm();
|
|
2014
|
-
}, { signal: sig });
|
|
2015
|
-
socket.addEventListener("message", (event) => {
|
|
2016
|
-
const config = handleMessage(event.data);
|
|
2017
|
-
if (!config) return;
|
|
2018
|
-
handshake.succeeded();
|
|
2019
|
-
onServerConfig(config);
|
|
2020
|
-
}, { signal: sig });
|
|
2021
|
-
socket.addEventListener("error", () => {
|
|
2022
|
-
socketErrored = true;
|
|
2023
|
-
}, { signal: sig });
|
|
2024
|
-
socket.addEventListener("close", (event) => {
|
|
2025
|
-
if (sig.aborted) return;
|
|
2026
|
-
handshake.disarm();
|
|
2027
|
-
audio.teardown();
|
|
2028
|
-
if (!(conn.retiredByServer || agentState.fatal()) && reconnectPending(socket)) {
|
|
2029
|
-
socketErrored = false;
|
|
2030
|
-
updateState({
|
|
2031
|
-
...agentState.apply({ type: "CONNECT" }),
|
|
2032
|
-
recording: false
|
|
2033
|
-
});
|
|
2034
|
-
return;
|
|
2035
|
-
}
|
|
2117
|
+
/**
|
|
2118
|
+
* The socket, the handshake deadline over it and the server's idle
|
|
2119
|
+
* retirement, as a statechart — see `session/connection.ts`. `release` is the
|
|
2120
|
+
* exit action of its `open` state, so every way a connection ends shares it.
|
|
2121
|
+
*/
|
|
2122
|
+
const connection = createConnection({
|
|
2123
|
+
dial: () => {
|
|
2124
|
+
preConnect.begin();
|
|
2125
|
+
const socket = dialer.open();
|
|
2126
|
+
socket.binaryType = "arraybuffer";
|
|
2127
|
+
conn.ws = socket;
|
|
2128
|
+
return socket;
|
|
2129
|
+
},
|
|
2130
|
+
release: (socket) => {
|
|
2036
2131
|
preConnect.release();
|
|
2037
|
-
|
|
2132
|
+
audio.teardown();
|
|
2038
2133
|
socket.close();
|
|
2039
2134
|
conn.ws = null;
|
|
2040
|
-
|
|
2135
|
+
},
|
|
2136
|
+
abandon: () => disconnect(),
|
|
2137
|
+
receive: handleMessage,
|
|
2138
|
+
opened: () => updateState(agentState.apply({ type: "SOCKET_OPEN" })),
|
|
2139
|
+
configured: onServerConfig,
|
|
2140
|
+
fatal: () => agentState.fatal(),
|
|
2141
|
+
retryPending: reconnectPending,
|
|
2142
|
+
canRedial: () => canReconnect(conn.ws),
|
|
2143
|
+
redial: () => {
|
|
2144
|
+
forceReconnect(conn.ws);
|
|
2145
|
+
},
|
|
2146
|
+
retrying: () => {
|
|
2147
|
+
audio.teardown();
|
|
2148
|
+
updateState({
|
|
2149
|
+
...agentState.apply({ type: "CONNECT" }),
|
|
2150
|
+
recording: false
|
|
2151
|
+
});
|
|
2152
|
+
},
|
|
2153
|
+
closed: (failure) => {
|
|
2041
2154
|
updateState({
|
|
2042
2155
|
...failure === null ? agentState.apply({ type: "CLOSED" }) : agentState.apply({
|
|
2043
2156
|
type: "FAILED",
|
|
@@ -2049,7 +2162,24 @@ function createBrowserSessionWith(options, internals) {
|
|
|
2049
2162
|
}),
|
|
2050
2163
|
...STOPPED
|
|
2051
2164
|
});
|
|
2052
|
-
},
|
|
2165
|
+
},
|
|
2166
|
+
exhausted: () => {
|
|
2167
|
+
updateState({
|
|
2168
|
+
...agentState.apply({
|
|
2169
|
+
type: "FAILED",
|
|
2170
|
+
error: HANDSHAKE_ERROR
|
|
2171
|
+
}),
|
|
2172
|
+
...STOPPED
|
|
2173
|
+
});
|
|
2174
|
+
}
|
|
2175
|
+
});
|
|
2176
|
+
function connect(opts) {
|
|
2177
|
+
if (opts?.signal?.aborted) {
|
|
2178
|
+
disconnect();
|
|
2179
|
+
return;
|
|
2180
|
+
}
|
|
2181
|
+
updateState(agentState.apply({ type: "CONNECT" }));
|
|
2182
|
+
connection.dial(opts?.signal);
|
|
2053
2183
|
}
|
|
2054
2184
|
function cancel() {
|
|
2055
2185
|
if (!openSocket()) return;
|
|
@@ -2079,7 +2209,7 @@ function createBrowserSessionWith(options, internals) {
|
|
|
2079
2209
|
start();
|
|
2080
2210
|
}
|
|
2081
2211
|
function disconnect() {
|
|
2082
|
-
|
|
2212
|
+
connection.hangUp();
|
|
2083
2213
|
updateState({
|
|
2084
2214
|
...agentState.apply({ type: "DISCONNECT" }),
|
|
2085
2215
|
...STOPPED
|
|
@@ -2100,7 +2230,7 @@ function createBrowserSessionWith(options, internals) {
|
|
|
2100
2230
|
}
|
|
2101
2231
|
}
|
|
2102
2232
|
function end() {
|
|
2103
|
-
|
|
2233
|
+
connection.hangUp();
|
|
2104
2234
|
dialer.forget();
|
|
2105
2235
|
updateState({
|
|
2106
2236
|
...CLEARED_SESSION_STATE,
|
package/dist/upload/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The workflow hooks' upload machinery, as the rest of the package sees it:
|
|
3
3
|
* claiming an id per file and transferring it (`files.ts`), the pause gate
|
|
4
|
-
* (`session.ts
|
|
4
|
+
* (`session.ts` — the buttons over it are the form's statechart,
|
|
5
|
+
* `../_workflow-form-state.ts`), the reload recall (`recall.ts`) and the report
|
|
5
6
|
* coalescing (`report.ts`). Everything not re-exported here is private to
|
|
6
7
|
* `upload/` — guard-invariants rule 37 fails an import from outside it that
|
|
7
8
|
* names any module but this one.
|
|
@@ -9,7 +10,6 @@
|
|
|
9
10
|
* @module
|
|
10
11
|
*/
|
|
11
12
|
export { createUploadSession, type UploadSession, uploadFiles } from "./files.ts";
|
|
12
|
-
export { useUploadPause } from "./pause.ts";
|
|
13
13
|
export { recallUploadId, rememberUploadId } from "./recall.ts";
|
|
14
14
|
export { coalesceUploadReports } from "./report.ts";
|
|
15
15
|
export { createUploadGate, randomUploadId, sendThroughGate, type UploadGate, } from "./session.ts";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alexkroman1/aai-ui",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"use-stick-to-bottom": "^1.1.6",
|
|
47
47
|
"use-sync-external-store": "^1.7.0",
|
|
48
48
|
"xstate": "^5.33.2",
|
|
49
|
-
"@alexkroman1/aai": "0.14.
|
|
49
|
+
"@alexkroman1/aai": "0.14.2"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@tailwindcss/vite": "^4.3.3",
|
package/dist/upload/pause.d.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The two buttons a page puts over a live upload, bound to whichever gate the
|
|
3
|
-
* submission is currently holding.
|
|
4
|
-
*
|
|
5
|
-
* `useWorkflowSubmit` and `useWorkflowStream` both own an {@link UploadGate} and
|
|
6
|
-
* both reported the pause the same way: park the gate, then fold `paused` into
|
|
7
|
-
* the status the bar is already drawing. Folded rather than replaced, because
|
|
8
|
-
* everything else about that status — which file, how far, of how many — is
|
|
9
|
-
* still true. Two copies of that rule are two copies that can stop agreeing
|
|
10
|
-
* about what a paused bar says.
|
|
11
|
-
*/
|
|
12
|
-
import { type Dispatch, type SetStateAction } from "react";
|
|
13
|
-
import type { UploadStatus } from "../use-workflow-form.ts";
|
|
14
|
-
import type { UploadGate } from "./session.ts";
|
|
15
|
-
/** What {@link useUploadPause} returns — see {@link WorkflowSubmission}. */
|
|
16
|
-
export type UploadPause = {
|
|
17
|
-
/** Stop the bytes in flight and hold the uploader. */
|
|
18
|
-
pauseUpload: () => void;
|
|
19
|
-
/** Send whatever the store is still missing. */
|
|
20
|
-
resumeUpload: () => void;
|
|
21
|
-
};
|
|
22
|
-
/**
|
|
23
|
-
* Bind pause/resume to the live submission's gate.
|
|
24
|
-
*
|
|
25
|
-
* @param getGate - Reads the gate out of the caller's ref, so the callbacks stay
|
|
26
|
-
* stable across submissions rather than being re-created per gate.
|
|
27
|
-
* @param setUpload - The status setter the progress bar renders from.
|
|
28
|
-
*
|
|
29
|
-
* @internal
|
|
30
|
-
*/
|
|
31
|
-
export declare function useUploadPause(getGate: () => UploadGate | undefined, setUpload: Dispatch<SetStateAction<UploadStatus | undefined>>): UploadPause;
|