@memberjunction/realtime-runtime 6.2.0-edge.4 → 6.2.0-edge.5

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.
Files changed (31) hide show
  1. package/dist/hosts/IRealtimeMediaHost.d.ts +25 -30
  2. package/dist/hosts/IRealtimeMediaHost.d.ts.map +1 -1
  3. package/dist/index.d.ts +5 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +4 -1
  6. package/dist/index.js.map +1 -1
  7. package/dist/session/RealtimeSessionRuntime.d.ts +285 -65
  8. package/dist/session/RealtimeSessionRuntime.d.ts.map +1 -1
  9. package/dist/session/RealtimeSessionRuntime.js +690 -165
  10. package/dist/session/RealtimeSessionRuntime.js.map +1 -1
  11. package/dist/session/RecordingShardDrain.d.ts +59 -0
  12. package/dist/session/RecordingShardDrain.d.ts.map +1 -0
  13. package/dist/session/RecordingShardDrain.js +135 -0
  14. package/dist/session/RecordingShardDrain.js.map +1 -0
  15. package/dist/session/RecordingShardQueue.d.ts +54 -37
  16. package/dist/session/RecordingShardQueue.d.ts.map +1 -1
  17. package/dist/session/RecordingShardQueue.js +87 -83
  18. package/dist/session/RecordingShardQueue.js.map +1 -1
  19. package/dist/session/RecordingShardStore.d.ts +57 -0
  20. package/dist/session/RecordingShardStore.d.ts.map +1 -0
  21. package/dist/session/RecordingShardStore.js +184 -0
  22. package/dist/session/RecordingShardStore.js.map +1 -0
  23. package/dist/session/RecordingShardWindow.d.ts +42 -0
  24. package/dist/session/RecordingShardWindow.d.ts.map +1 -0
  25. package/dist/session/RecordingShardWindow.js +88 -0
  26. package/dist/session/RecordingShardWindow.js.map +1 -0
  27. package/dist/session/RecordingTransport.d.ts +66 -0
  28. package/dist/session/RecordingTransport.d.ts.map +1 -0
  29. package/dist/session/RecordingTransport.js +61 -0
  30. package/dist/session/RecordingTransport.js.map +1 -0
  31. package/package.json +10 -9
@@ -0,0 +1,135 @@
1
+ import { CreateRecordingShardStore } from './RecordingShardStore.js';
2
+ import { FetchRecordingTransport, RecordingBaseURLFromGraphQLURL } from './RecordingTransport.js';
3
+ /** Most stored shards one drain pass re-uploads; the rest wait for the next pass or load. */
4
+ export const MAX_DRAINED_SHARDS_PER_LOAD = 50;
5
+ function describeFailure(failure) {
6
+ return failure instanceof Error ? failure.message : String(failure);
7
+ }
8
+ /**
9
+ * The default recording transport for a provider: MJAPI's REST recording routes, derived from the
10
+ * provider's GraphQL URL, with credentials read per request so a refreshed token is used. Null when
11
+ * the provider has no URL (a test double, a provider that is not GraphQL, or one not configured yet).
12
+ */
13
+ export function CreateRecordingTransportForProvider(provider) {
14
+ // Typed as GraphQL, but read defensively: a test double or a non-GraphQL provider has no ConfigData.
15
+ const graphQL = provider;
16
+ const url = graphQL?.ConfigData?.URL;
17
+ if (!graphQL || !url) {
18
+ return null;
19
+ }
20
+ return new FetchRecordingTransport({
21
+ BaseURL: RecordingBaseURLFromGraphQLURL(url),
22
+ Headers: () => {
23
+ const config = graphQL.ConfigData;
24
+ const headers = {};
25
+ if (config?.Token)
26
+ headers.authorization = `Bearer ${config.Token}`;
27
+ if (config?.MJAPIKey)
28
+ headers['x-mj-api-key'] = config.MJAPIKey;
29
+ if (config?.UserAPIKey)
30
+ headers['x-api-key'] = config.UserAPIKey;
31
+ return headers;
32
+ },
33
+ });
34
+ }
35
+ /**
36
+ * One drain pass over `userID`'s stored shards, oldest first, each under its own session and index. A
37
+ * shard the server acknowledges or refuses for good (see {@link RecordingUploadOutcome}) leaves the
38
+ * store; one that may still succeed stays for the next pass or load. Never rejects for an upload or
39
+ * delete failure (each is logged); a store whose listing throws rejects.
40
+ */
41
+ export async function DrainStoredRecordingShardsOnce(store, transport, userID) {
42
+ const pending = await store.ListOldest(MAX_DRAINED_SHARDS_PER_LOAD, userID);
43
+ let delivered = 0;
44
+ let refused = 0;
45
+ for (const shard of pending) {
46
+ let outcome = 'Retry';
47
+ try {
48
+ outcome = await transport.UploadShard(shard.SessionID, shard);
49
+ }
50
+ catch (error) {
51
+ console.warn(`[RealtimeSession] Re-uploading stored recording shard ${shard.SessionID}/${shard.Index} failed: ${describeFailure(error)}; it stays stored for the next load.`, error);
52
+ }
53
+ if (outcome === 'Acked') {
54
+ await DeleteStoredRecordingShard(store, shard.SessionID, shard.Index);
55
+ delivered++;
56
+ }
57
+ else if (outcome === 'Refused') {
58
+ console.warn(`[RealtimeSession] The server refused stored recording shard ${shard.SessionID}/${shard.Index} for good; deleting it so it no longer holds back the shards behind it.`);
59
+ await DeleteStoredRecordingShard(store, shard.SessionID, shard.Index);
60
+ refused++;
61
+ }
62
+ }
63
+ if (pending.length > 0) {
64
+ console.log(`[RealtimeSession] Re-uploaded ${delivered}/${pending.length} stored recording shard(s); ${refused} refused and deleted.`);
65
+ }
66
+ return { Listed: pending.length, Delivered: delivered, Refused: refused };
67
+ }
68
+ /** Removes one shard from the store. Never rejects: a failed delete only costs a duplicate re-upload. */
69
+ export async function DeleteStoredRecordingShard(store, sessionId, index) {
70
+ try {
71
+ await store.Delete(sessionId, index);
72
+ }
73
+ catch (error) {
74
+ // Harmless beyond a wasted re-upload on the next load: the server acks a duplicate.
75
+ console.warn(`[RealtimeSession] Failed to delete recording shard ${sessionId}/${index} from the local store:`, error);
76
+ }
77
+ }
78
+ /** Most drain passes a logout runs before it deletes what is left (each pass is itself bounded). */
79
+ const MAX_LOGOUT_DRAIN_PASSES = 3;
80
+ /**
81
+ * For logout: tries to deliver `userID`'s stored shards within `timeoutMs`, then deletes every shard
82
+ * of that user that is still stored, so their call audio neither stays on a shared browser's disk nor
83
+ * waits for a later login. Other users' shards are untouched. Never rejects and never takes longer
84
+ * than `timeoutMs` plus the delete.
85
+ */
86
+ export async function ReleaseUserRecordingShards(store, transport, userID, timeoutMs) {
87
+ let delivered = 0;
88
+ let timedOut = false;
89
+ if (transport) {
90
+ let timer;
91
+ const deadline = new Promise((resolve) => {
92
+ timer = setTimeout(() => resolve('timeout'), timeoutMs);
93
+ });
94
+ const attempt = (async () => {
95
+ for (let pass = 1; pass <= MAX_LOGOUT_DRAIN_PASSES; pass++) {
96
+ const result = await DrainStoredRecordingShardsOnce(store, transport, userID);
97
+ delivered += result.Delivered;
98
+ // Another pass only reaches shards beyond this pass's page, and only when this one made room;
99
+ // a shard that answered Retry is not retried within the logout.
100
+ if (result.Listed < MAX_DRAINED_SHARDS_PER_LOAD || result.Delivered + result.Refused === 0) {
101
+ return;
102
+ }
103
+ }
104
+ })().catch((error) => {
105
+ console.warn(`[RealtimeSession] Sending stored recording shards at logout failed for user ${userID ?? '(none)'}: ${describeFailure(error)}; deleting them.`, error);
106
+ });
107
+ try {
108
+ timedOut = (await Promise.race([attempt, deadline])) === 'timeout';
109
+ }
110
+ finally {
111
+ clearTimeout(timer);
112
+ }
113
+ if (timedOut) {
114
+ console.warn(`[RealtimeSession] Sending stored recording shards at logout took over ${timeoutMs} ms for user ${userID ?? '(none)'}; deleting what is left.`);
115
+ }
116
+ }
117
+ try {
118
+ await store.DeleteForUser(userID);
119
+ }
120
+ catch (error) {
121
+ console.warn(`[RealtimeSession] Failed to delete the stored recording shards of user ${userID ?? '(none)'} at logout:`, error);
122
+ }
123
+ return { Delivered: delivered, TimedOut: timedOut };
124
+ }
125
+ /** How long a logout waits for stored shards to upload before it deletes them. */
126
+ export const RECORDING_LOGOUT_SEND_TIMEOUT_MS = 3000;
127
+ /**
128
+ * A host's logout step: {@link ReleaseUserRecordingShards} for the provider's signed-in user, with the
129
+ * browser's IndexedDB store and the provider's recording transport. Call it while the provider is
130
+ * still authenticated (before its token is dropped). Never rejects.
131
+ */
132
+ export function ReleaseRecordingShardsAtLogout(provider, timeoutMs = RECORDING_LOGOUT_SEND_TIMEOUT_MS) {
133
+ return ReleaseUserRecordingShards(CreateRecordingShardStore(globalThis.indexedDB), CreateRecordingTransportForProvider(provider), provider?.CurrentUser?.ID ?? null, timeoutMs);
134
+ }
135
+ //# sourceMappingURL=RecordingShardDrain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RecordingShardDrain.js","sourceRoot":"","sources":["../../src/session/RecordingShardDrain.ts"],"names":[],"mappings":"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"}
@@ -1,76 +1,93 @@
1
1
  /**
2
- * @fileoverview Retry queue for crash-recovery audio shards.
2
+ * @fileoverview Retry queue for indexed recording shards.
3
3
  *
4
- * The browser recorder is snapshotted every ~15 s and each snapshot ("shard") is uploaded so a tab
5
- * crash loses at most one window of audio. Before this queue a failed upload was never retried, so a
6
- * single transient error left a permanent gap in the recovered recording (#5197). The queue keeps
7
- * failed shards and resends them, oldest first, on later flushes — bounded so a dead server cannot
8
- * grow memory without limit.
4
+ * The recorder cuts the call's Opus stream into already-indexed `MJOPUS1` shards (see
5
+ * {@link RecordingShardWindow}) and each one is uploaded as it seals. Before this queue a failed
6
+ * upload was never retried, so a single transient error left a permanent gap in the recovered
7
+ * recording (#5197). The queue keeps failed shards and resends them, oldest first, on later
8
+ * flushes — bounded so a dead server cannot grow memory without limit. A shard the queue gives up on
9
+ * is not lost: it stays in the IndexedDB store, and the owner is told so it can re-send it from there
10
+ * once uploads succeed again (or a later load drains it).
9
11
  *
10
12
  * @module @memberjunction/realtime-runtime
11
13
  */
12
- /** Takes the audio recorded since the previous snapshot as base64, or null/'' when there is none. */
13
- export type ShardSnapshot = () => Promise<string | null>;
14
+ import type { RecordingShard } from './RecordingShardWindow.js';
14
15
  /** Resolves true when the server stored the shard; false or a throw are both failures. */
15
- export type ShardUpload = (segmentIndex: number, audioBase64: string) => Promise<boolean>;
16
+ export type ShardUpload = (shard: RecordingShard) => Promise<boolean>;
17
+ /**
18
+ * Told about shards the queue stops retrying while the recording is live (attempts used up, or the
19
+ * retry list overflowed). They are still in the store; the owner re-sends them once uploads work.
20
+ */
21
+ export type ShardsGivenUp = (shards: RecordingShard[]) => void;
16
22
  /** Total upload attempts per shard (the first try plus retries) before it is dropped. */
17
23
  export declare const MAX_SHARD_UPLOAD_ATTEMPTS = 3;
18
24
  /**
19
- * Flushes allowed in flight at once. Ticks are 15 s apart, so overlap only happens when an upload is
20
- * slower than a tick; an unbounded pile-up on a dead network would snapshot (and hold in memory) a
21
- * new window every tick. A skipped tick costs nothing: the recorder keeps the audio and the next
22
- * flush ships the larger window. While both slots are pinned the recorder cursor does not advance,
23
- * so the next capture is one larger window (bounded by the upload timeout x attempts); if that
24
- * shard is later dropped, recovery fills a correspondingly longer gap with silence.
25
+ * Flushes allowed in flight at once. Flushes are ~5 s apart, so overlap only happens when an upload
26
+ * is slower than a tick; an unbounded pile-up on a dead network would start a new batch of uploads
27
+ * every tick. A flush that arrives at capacity does not upload, but it does not lose its shards
28
+ * either: they are retained and the next flush that has a free slot sends them, oldest first.
25
29
  */
26
30
  export declare const MAX_CONCURRENT_FLUSHES = 2;
27
31
  /**
28
32
  * Per-attempt upload deadline. A hung request would otherwise pin one of the
29
33
  * {@link MAX_CONCURRENT_FLUSHES} slots forever and silently stop all later shards. A timeout counts
30
34
  * as a failed attempt; if the request later succeeds anyway that is harmless, because the server
31
- * key is per-index and a resend is an idempotent overwrite. The timeout is logical only:
32
- * `ExecuteGQL` accepts no AbortSignal, so a timed-out request may still be on the wire. Requests
33
- * stay rate-bounded regardless (at most {@link MAX_CONCURRENT_FLUSHES} flushes, each sending at
34
- * most one batch, per timeout window), and a late success is an idempotent same-index overwrite.
35
+ * key is per-index and a resend of the same bytes is an idempotent overwrite. The timeout is
36
+ * logical only: the transport may accept no AbortSignal, so a timed-out request may still be on the
37
+ * wire. Requests stay rate-bounded regardless (at most {@link MAX_CONCURRENT_FLUSHES} flushes, each
38
+ * sending at most one batch, per timeout window).
35
39
  */
36
40
  export declare const SHARD_UPLOAD_TIMEOUT_MS = 60000;
37
41
  /**
38
- * Assigns chronological indexes to crash-recovery shards, uploads them, and retains failures for
39
- * retry on later flushes. Safe to call {@link Flush} from an overlapping timer.
42
+ * Cap on shards held for retry: 10 minutes of 5 s shards. A dead network with flushes at capacity
43
+ * would otherwise grow the retained list (and its bytes) without bound. Overflow drops the oldest;
44
+ * dropped shards remain in the IndexedDB store for the next-load drain.
45
+ */
46
+ export declare const MAX_RETAINED_SHARDS = 120;
47
+ /**
48
+ * Uploads indexed shards and retains failures for retry on later flushes. A retry keeps its shard's
49
+ * index and bytes. Safe to call {@link Flush} from an overlapping timer.
40
50
  */
41
51
  export declare class RecordingShardQueue {
42
- private nextIndex;
52
+ private readonly upload;
53
+ private readonly onGivenUp;
43
54
  private retained;
55
+ /** Every shard handed to the queue and not yet acknowledged or given up on, by index. */
56
+ private readonly outstanding;
44
57
  private inFlight;
45
58
  /** True once a capacity stall has been logged; cleared when a flush actually runs. */
46
59
  private capacityWarned;
47
- /** Set by {@link Close} when the recording ends; nothing is snapshotted, sent or retained after it. */
60
+ /** Set by {@link Close} when the recording ends; nothing is sent or retained after it. */
48
61
  private closed;
49
62
  /**
50
- * Serializes "take a snapshot, then assign an index". The recorder advances its cursor
51
- * synchronously but encodes asynchronously, so two overlapping snapshots could resolve out of
52
- * order and give newer audio an older index. Never rejected: the link catches internally.
63
+ * @param upload sends one shard
64
+ * @param onGivenUp told about every shard dropped while the recording is live (not on {@link Close})
53
65
  */
54
- private captureChain;
66
+ constructor(upload: ShardUpload, onGivenUp?: ShardsGivenUp);
55
67
  /** Shards awaiting a retry (for tests and diagnostics). */
56
68
  get RetainedCount(): number;
57
69
  /**
58
- * Takes ownership of retained shards and uploads them oldest-first, then captures a new window
59
- * and uploads it. Retained shards go first so a hung snapshot cannot strand them. A failure never
60
- * stops the rest of the flush. Skips entirely (no snapshot) when
61
- * {@link MAX_CONCURRENT_FLUSHES} are already running. Never rejects.
70
+ * Every shard the queue still holds without an acknowledgement — on the wire, waiting in a flush's
71
+ * batch, or retained for a retry — oldest first. A page that is about to unload re-sends these with
72
+ * `keepalive`, because its ordinary requests are aborted with it.
73
+ */
74
+ Unacknowledged(): RecordingShard[];
75
+ /**
76
+ * Uploads retained shards oldest-first, then `fresh`. A failure never stops the rest of the
77
+ * flush. At {@link MAX_CONCURRENT_FLUSHES} in flight nothing is uploaded and `fresh` is retained
78
+ * for the next flush instead. Never rejects.
62
79
  */
63
- Flush(snapshot: ShardSnapshot, upload: ShardUpload): Promise<void>;
80
+ Flush(fresh: RecordingShard[]): Promise<void>;
64
81
  /**
65
82
  * Ends the queue with its recording: discards retained shards and stops any flush in flight from
66
- * sending more. An upload already on the wire cannot be aborted (`ExecuteGQL` takes no
67
- * AbortSignal), so at most that one request can still land.
83
+ * sending more. An upload already on the wire cannot be aborted, so at most that one request can
84
+ * still land. Discarded shards remain in the IndexedDB store and are drained on a later load.
68
85
  */
69
86
  Close(): void;
70
- /** Warns once per stall so a wedged uploader is visible without logging every 15 s tick. */
87
+ /** Sorted re-insert: concurrent flushes can fail out of index order, and the oldest must go first. */
88
+ private retain;
89
+ /** Warns once per stall so a wedged uploader is visible without logging every tick. */
71
90
  private warnCapacitySkip;
72
- private captureShard;
73
- private takeIndexedSnapshot;
74
91
  private uploadWithTimeout;
75
92
  private send;
76
93
  }
@@ -1 +1 @@
1
- {"version":3,"file":"RecordingShardQueue.d.ts","sourceRoot":"","sources":["../../src/session/RecordingShardQueue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,qGAAqG;AACrG,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAEzD,0FAA0F;AAC1F,MAAM,MAAM,WAAW,GAAG,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAE1F,yFAAyF;AACzF,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,QAAS,CAAC;AAa9C;;;GAGG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,QAAQ,CAAK;IACrB,sFAAsF;IACtF,OAAO,CAAC,cAAc,CAAS;IAC/B,uGAAuG;IACvG,OAAO,CAAC,MAAM,CAAS;IACvB;;;;OAIG;IACH,OAAO,CAAC,YAAY,CAAoC;IAExD,2DAA2D;IAC3D,IAAW,aAAa,IAAI,MAAM,CAEjC;IAED;;;;;OAKG;IACU,KAAK,CAAC,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IA+B/E;;;;OAIG;IACI,KAAK,IAAI,IAAI;IAKpB,4FAA4F;IAC5F,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,YAAY;YAMN,mBAAmB;YAanB,iBAAiB;YAYjB,IAAI;CAkCnB"}
1
+ {"version":3,"file":"RecordingShardQueue.d.ts","sourceRoot":"","sources":["../../src/session/RecordingShardQueue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,0FAA0F;AAC1F,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtE;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,IAAI,CAAC;AAE/D,yFAAyF;AACzF,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,QAAS,CAAC;AAE9C;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAYvC;;;GAGG;AACH,qBAAa,mBAAmB;IAclB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAAe,OAAO,CAAC,QAAQ,CAAC,SAAS;IAb5E,OAAO,CAAC,QAAQ,CAAsB;IACtC,yFAAyF;IACzF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqC;IACjE,OAAO,CAAC,QAAQ,CAAK;IACrB,sFAAsF;IACtF,OAAO,CAAC,cAAc,CAAS;IAC/B,0FAA0F;IAC1F,OAAO,CAAC,MAAM,CAAS;IAEvB;;;OAGG;gBAC0B,MAAM,EAAE,WAAW,EAAmB,SAAS,GAAE,aAA+B;IAE7G,2DAA2D;IAC3D,IAAW,aAAa,IAAI,MAAM,CAEjC;IAED;;;;OAIG;IACI,cAAc,IAAI,cAAc,EAAE;IAIzC;;;;OAIG;IACU,KAAK,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAqC1D;;;;OAIG;IACI,KAAK,IAAI,IAAI;IAMpB,sGAAsG;IACtG,OAAO,CAAC,MAAM;IAgBd,uFAAuF;IACvF,OAAO,CAAC,gBAAgB;YAUV,iBAAiB;YAYjB,IAAI;CAoCnB"}
@@ -1,71 +1,83 @@
1
- /**
2
- * @fileoverview Retry queue for crash-recovery audio shards.
3
- *
4
- * The browser recorder is snapshotted every ~15 s and each snapshot ("shard") is uploaded so a tab
5
- * crash loses at most one window of audio. Before this queue a failed upload was never retried, so a
6
- * single transient error left a permanent gap in the recovered recording (#5197). The queue keeps
7
- * failed shards and resends them, oldest first, on later flushes — bounded so a dead server cannot
8
- * grow memory without limit.
9
- *
10
- * @module @memberjunction/realtime-runtime
11
- */
12
1
  /** Total upload attempts per shard (the first try plus retries) before it is dropped. */
13
2
  export const MAX_SHARD_UPLOAD_ATTEMPTS = 3;
14
3
  /**
15
- * Flushes allowed in flight at once. Ticks are 15 s apart, so overlap only happens when an upload is
16
- * slower than a tick; an unbounded pile-up on a dead network would snapshot (and hold in memory) a
17
- * new window every tick. A skipped tick costs nothing: the recorder keeps the audio and the next
18
- * flush ships the larger window. While both slots are pinned the recorder cursor does not advance,
19
- * so the next capture is one larger window (bounded by the upload timeout x attempts); if that
20
- * shard is later dropped, recovery fills a correspondingly longer gap with silence.
4
+ * Flushes allowed in flight at once. Flushes are ~5 s apart, so overlap only happens when an upload
5
+ * is slower than a tick; an unbounded pile-up on a dead network would start a new batch of uploads
6
+ * every tick. A flush that arrives at capacity does not upload, but it does not lose its shards
7
+ * either: they are retained and the next flush that has a free slot sends them, oldest first.
21
8
  */
22
9
  export const MAX_CONCURRENT_FLUSHES = 2;
23
10
  /**
24
11
  * Per-attempt upload deadline. A hung request would otherwise pin one of the
25
12
  * {@link MAX_CONCURRENT_FLUSHES} slots forever and silently stop all later shards. A timeout counts
26
13
  * as a failed attempt; if the request later succeeds anyway that is harmless, because the server
27
- * key is per-index and a resend is an idempotent overwrite. The timeout is logical only:
28
- * `ExecuteGQL` accepts no AbortSignal, so a timed-out request may still be on the wire. Requests
29
- * stay rate-bounded regardless (at most {@link MAX_CONCURRENT_FLUSHES} flushes, each sending at
30
- * most one batch, per timeout window), and a late success is an idempotent same-index overwrite.
14
+ * key is per-index and a resend of the same bytes is an idempotent overwrite. The timeout is
15
+ * logical only: the transport may accept no AbortSignal, so a timed-out request may still be on the
16
+ * wire. Requests stay rate-bounded regardless (at most {@link MAX_CONCURRENT_FLUSHES} flushes, each
17
+ * sending at most one batch, per timeout window).
31
18
  */
32
19
  export const SHARD_UPLOAD_TIMEOUT_MS = 60_000;
20
+ /**
21
+ * Cap on shards held for retry: 10 minutes of 5 s shards. A dead network with flushes at capacity
22
+ * would otherwise grow the retained list (and its bytes) without bound. Overflow drops the oldest;
23
+ * dropped shards remain in the IndexedDB store for the next-load drain.
24
+ */
25
+ export const MAX_RETAINED_SHARDS = 120;
33
26
  /** One-line reason for a warn message; the raw value is still passed along for its stack. */
34
27
  function describeFailure(failure) {
35
28
  return failure instanceof Error ? failure.message : String(failure);
36
29
  }
37
30
  /**
38
- * Assigns chronological indexes to crash-recovery shards, uploads them, and retains failures for
39
- * retry on later flushes. Safe to call {@link Flush} from an overlapping timer.
31
+ * Uploads indexed shards and retains failures for retry on later flushes. A retry keeps its shard's
32
+ * index and bytes. Safe to call {@link Flush} from an overlapping timer.
40
33
  */
41
34
  export class RecordingShardQueue {
42
- constructor() {
43
- this.nextIndex = 0;
35
+ /**
36
+ * @param upload sends one shard
37
+ * @param onGivenUp told about every shard dropped while the recording is live (not on {@link Close})
38
+ */
39
+ constructor(upload, onGivenUp = () => undefined) {
40
+ this.upload = upload;
41
+ this.onGivenUp = onGivenUp;
44
42
  this.retained = [];
43
+ /** Every shard handed to the queue and not yet acknowledged or given up on, by index. */
44
+ this.outstanding = new Map();
45
45
  this.inFlight = 0;
46
46
  /** True once a capacity stall has been logged; cleared when a flush actually runs. */
47
47
  this.capacityWarned = false;
48
- /** Set by {@link Close} when the recording ends; nothing is snapshotted, sent or retained after it. */
48
+ /** Set by {@link Close} when the recording ends; nothing is sent or retained after it. */
49
49
  this.closed = false;
50
- /**
51
- * Serializes "take a snapshot, then assign an index". The recorder advances its cursor
52
- * synchronously but encodes asynchronously, so two overlapping snapshots could resolve out of
53
- * order and give newer audio an older index. Never rejected: the link catches internally.
54
- */
55
- this.captureChain = Promise.resolve();
56
50
  }
57
51
  /** Shards awaiting a retry (for tests and diagnostics). */
58
52
  get RetainedCount() {
59
53
  return this.retained.length;
60
54
  }
61
55
  /**
62
- * Takes ownership of retained shards and uploads them oldest-first, then captures a new window
63
- * and uploads it. Retained shards go first so a hung snapshot cannot strand them. A failure never
64
- * stops the rest of the flush. Skips entirely (no snapshot) when
65
- * {@link MAX_CONCURRENT_FLUSHES} are already running. Never rejects.
56
+ * Every shard the queue still holds without an acknowledgement — on the wire, waiting in a flush's
57
+ * batch, or retained for a retry — oldest first. A page that is about to unload re-sends these with
58
+ * `keepalive`, because its ordinary requests are aborted with it.
66
59
  */
67
- async Flush(snapshot, upload) {
60
+ Unacknowledged() {
61
+ return [...this.outstanding.values()].sort((a, b) => a.Index - b.Index);
62
+ }
63
+ /**
64
+ * Uploads retained shards oldest-first, then `fresh`. A failure never stops the rest of the
65
+ * flush. At {@link MAX_CONCURRENT_FLUSHES} in flight nothing is uploaded and `fresh` is retained
66
+ * for the next flush instead. Never rejects.
67
+ */
68
+ async Flush(fresh) {
69
+ if (this.closed) {
70
+ if (fresh.length > 0) {
71
+ console.warn(`[RealtimeSession] Recording flush after the recording ended; shard(s) ${fresh.map((s) => s.Index).join(', ')} not sent — they stay in the local store and are drained later.`);
72
+ }
73
+ return;
74
+ }
75
+ const freshPending = fresh.map((shard) => ({ Shard: shard, Attempts: 0 }));
76
+ for (const shard of fresh) {
77
+ this.outstanding.set(shard.Index, shard);
78
+ }
68
79
  if (this.inFlight >= MAX_CONCURRENT_FLUSHES) {
80
+ this.retain(freshPending);
69
81
  this.warnCapacitySkip();
70
82
  return;
71
83
  }
@@ -73,22 +85,15 @@ export class RecordingShardQueue {
73
85
  this.inFlight++;
74
86
  try {
75
87
  // Swap the array out so a concurrent flush only sees shards nobody is currently sending.
76
- const batch = this.retained;
88
+ const batch = [...this.retained, ...freshPending].sort((a, b) => a.Shard.Index - b.Shard.Index);
77
89
  this.retained = [];
78
- // Retained shards go first, BEFORE the capture: a snapshot that never resolves must not strand
79
- // shards that already failed. Index order is unaffected — indexes are assigned in capture order
80
- // on the serialized chain, and every retained shard already has an older index.
81
- // `closed` is re-checked before every send: a flush running when the recording ends must not
82
- // keep uploading, or a resend could land after the end-of-call upload has deleted the shards.
83
- for (const shard of batch) {
90
+ for (const pending of batch) {
91
+ // Re-checked before every send: a flush running when the recording ends must not keep
92
+ // uploading shards the terminal request is about to supersede.
84
93
  if (this.closed) {
85
94
  return;
86
95
  }
87
- await this.send(shard, upload);
88
- }
89
- const fresh = this.closed ? null : await this.captureShard(snapshot);
90
- if (fresh && !this.closed) {
91
- await this.send(fresh, upload);
96
+ await this.send(pending);
92
97
  }
93
98
  }
94
99
  finally {
@@ -97,76 +102,75 @@ export class RecordingShardQueue {
97
102
  }
98
103
  /**
99
104
  * Ends the queue with its recording: discards retained shards and stops any flush in flight from
100
- * sending more. An upload already on the wire cannot be aborted (`ExecuteGQL` takes no
101
- * AbortSignal), so at most that one request can still land.
105
+ * sending more. An upload already on the wire cannot be aborted, so at most that one request can
106
+ * still land. Discarded shards remain in the IndexedDB store and are drained on a later load.
102
107
  */
103
108
  Close() {
104
109
  this.closed = true;
105
110
  this.retained = [];
111
+ this.outstanding.clear();
112
+ }
113
+ /** Sorted re-insert: concurrent flushes can fail out of index order, and the oldest must go first. */
114
+ retain(pending) {
115
+ this.retained.push(...pending);
116
+ this.retained.sort((a, b) => a.Shard.Index - b.Shard.Index);
117
+ const overflow = this.retained.length - MAX_RETAINED_SHARDS;
118
+ if (overflow > 0) {
119
+ const dropped = this.retained.splice(0, overflow);
120
+ for (const pending of dropped) {
121
+ this.outstanding.delete(pending.Shard.Index);
122
+ }
123
+ console.warn(`[RealtimeSession] Retry queue over ${MAX_RETAINED_SHARDS} shards; dropped oldest shard(s) ${dropped.map((p) => p.Shard.Index).join(', ')} from memory — they stay in the local IndexedDB store and are re-sent from it once uploads succeed again.`);
124
+ this.onGivenUp(dropped.map((p) => p.Shard));
125
+ }
106
126
  }
107
- /** Warns once per stall so a wedged uploader is visible without logging every 15 s tick. */
127
+ /** Warns once per stall so a wedged uploader is visible without logging every tick. */
108
128
  warnCapacitySkip() {
109
129
  if (this.capacityWarned) {
110
130
  return;
111
131
  }
112
132
  this.capacityWarned = true;
113
- console.warn(`[RealtimeSession] Recording flush skipped: at capacity with ${this.inFlight}/${MAX_CONCURRENT_FLUSHES} flushes in flight and ${this.retained.length} shard(s) retained. The recorder keeps the audio for the next flush.`);
114
- }
115
- captureShard(snapshot) {
116
- const capture = this.captureChain.then(() => this.takeIndexedSnapshot(snapshot));
117
- this.captureChain = capture.then(() => undefined);
118
- return capture;
119
- }
120
- async takeIndexedSnapshot(snapshot) {
121
- try {
122
- const audio = await snapshot();
123
- if (!audio) {
124
- return null;
125
- }
126
- return { Index: this.nextIndex++, AudioBase64: audio, Attempts: 0 };
127
- }
128
- catch (error) {
129
- console.warn('[RealtimeSession] Failed to snapshot recording shard:', error);
130
- return null;
131
- }
133
+ console.warn(`[RealtimeSession] Recording flush at capacity: ${this.inFlight}/${MAX_CONCURRENT_FLUSHES} flushes in flight, ${this.retained.length} shard(s) retained. Retained shards are sent by the next flush with a free slot.`);
132
134
  }
133
- async uploadWithTimeout(upload, shard) {
135
+ async uploadWithTimeout(shard) {
134
136
  let timer;
135
137
  const deadline = new Promise((_, reject) => {
136
138
  timer = setTimeout(() => reject(new Error(`upload timed out after ${SHARD_UPLOAD_TIMEOUT_MS}ms`)), SHARD_UPLOAD_TIMEOUT_MS);
137
139
  });
138
140
  try {
139
- return await Promise.race([upload(shard.Index, shard.AudioBase64), deadline]);
141
+ return await Promise.race([this.upload(shard), deadline]);
140
142
  }
141
143
  finally {
142
144
  clearTimeout(timer);
143
145
  }
144
146
  }
145
- async send(shard, upload) {
146
- shard.Attempts++;
147
+ async send(pending) {
148
+ const { Shard: shard } = pending;
149
+ pending.Attempts++;
147
150
  let stored = false;
148
151
  let failure = 'server refused the shard';
149
152
  try {
150
- stored = await this.uploadWithTimeout(upload, shard);
153
+ stored = await this.uploadWithTimeout(shard);
151
154
  }
152
155
  catch (error) {
153
156
  failure = error;
154
157
  }
155
158
  if (stored) {
159
+ this.outstanding.delete(shard.Index);
156
160
  return;
157
161
  }
158
162
  if (this.closed) {
159
- console.warn(`[RealtimeSession] Recording shard ${shard.Index} upload failed after the recording ended (${describeFailure(failure)}); not retried — the end-of-call upload supersedes it:`, failure);
163
+ console.warn(`[RealtimeSession] Recording shard ${shard.Index} upload failed after the recording ended (${describeFailure(failure)}); not retried — it stays in the local store and is drained later:`, failure);
160
164
  return;
161
165
  }
162
- if (shard.Attempts >= MAX_SHARD_UPLOAD_ATTEMPTS) {
163
- console.warn(`[RealtimeSession] Dropping recording shard ${shard.Index} after ${shard.Attempts}/${MAX_SHARD_UPLOAD_ATTEMPTS} failed uploads (${describeFailure(failure)}); recovery will fill the gap with silence:`, failure);
166
+ if (pending.Attempts >= MAX_SHARD_UPLOAD_ATTEMPTS) {
167
+ console.warn(`[RealtimeSession] Dropping recording shard ${shard.Index} from the retry queue after ${pending.Attempts}/${MAX_SHARD_UPLOAD_ATTEMPTS} failed uploads (${describeFailure(failure)}); it stays in the local store and is re-sent from it once uploads succeed again:`, failure);
168
+ this.outstanding.delete(shard.Index);
169
+ this.onGivenUp([shard]);
164
170
  return;
165
171
  }
166
- console.warn(`[RealtimeSession] Recording shard ${shard.Index} upload failed (attempt ${shard.Attempts}/${MAX_SHARD_UPLOAD_ATTEMPTS}: ${describeFailure(failure)}); will retry next flush:`, failure);
167
- // Sorted re-insert: concurrent flushes can fail out of index order, and the oldest must go first.
168
- this.retained.push(shard);
169
- this.retained.sort((a, b) => a.Index - b.Index);
172
+ console.warn(`[RealtimeSession] Recording shard ${shard.Index} upload failed (attempt ${pending.Attempts}/${MAX_SHARD_UPLOAD_ATTEMPTS}: ${describeFailure(failure)}); will retry next flush:`, failure);
173
+ this.retain([pending]);
170
174
  }
171
175
  }
172
176
  //# sourceMappingURL=RecordingShardQueue.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RecordingShardQueue.js","sourceRoot":"","sources":["../../src/session/RecordingShardQueue.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"RecordingShardQueue.js","sourceRoot":"","sources":["../../src/session/RecordingShardQueue.ts"],"names":[],"mappings":"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"}