@camera.ui/transport 0.0.22 → 0.0.24
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.
|
@@ -8,7 +8,9 @@ export interface DegradedRecoveryOptions {
|
|
|
8
8
|
readonly ensureAll: () => void;
|
|
9
9
|
readonly probe: () => Promise<BackgroundProbeOutcome>;
|
|
10
10
|
readonly graceMs?: number;
|
|
11
|
+
readonly isReachable?: () => boolean;
|
|
11
12
|
readonly onEscalate?: (round: number) => void;
|
|
12
13
|
readonly onOffline?: (reason: string) => void;
|
|
14
|
+
readonly onHold?: (reason: string) => void;
|
|
13
15
|
}
|
|
14
16
|
export declare function attachDegradedRecovery(options: DegradedRecoveryOptions): Detach;
|
|
@@ -6,5 +6,6 @@ export interface NetworkChangeOptions {
|
|
|
6
6
|
readonly kernel: Kernel;
|
|
7
7
|
readonly source: NetworkChangeSource;
|
|
8
8
|
readonly onChange: (kernel: Kernel, event: Event) => void;
|
|
9
|
+
readonly debounceMs?: number;
|
|
9
10
|
}
|
|
10
11
|
export declare function attachNetworkChange(options: NetworkChangeOptions): Detach;
|
|
@@ -5,11 +5,13 @@ export type RefreshReason = 'proactive' | 'auth-error';
|
|
|
5
5
|
export interface TokenLifecycleOptions {
|
|
6
6
|
readonly kernel: Kernel;
|
|
7
7
|
readonly transports: readonly Transport[];
|
|
8
|
-
readonly refresh: (target: ConnectionTarget, reason: RefreshReason) => Promise<Tokens>;
|
|
9
8
|
readonly graceMs?: number;
|
|
10
|
-
readonly isTransientError?: (err: unknown) => boolean;
|
|
11
9
|
readonly maxTransientRetries?: number;
|
|
12
10
|
readonly transientRetryDelayMs?: number;
|
|
11
|
+
readonly maxServerErrorRetries?: number;
|
|
12
|
+
readonly refresh: (target: ConnectionTarget, reason: RefreshReason) => Promise<Tokens>;
|
|
13
|
+
readonly isTransientError?: (err: unknown) => boolean;
|
|
14
|
+
readonly isServerError?: (err: unknown) => boolean;
|
|
13
15
|
readonly now?: () => number;
|
|
14
16
|
readonly setTimer?: (cb: () => void, ms: number) => unknown;
|
|
15
17
|
readonly clearTimer?: (handle: unknown) => void;
|
|
@@ -20,6 +22,7 @@ export interface TokenLifecycleOptions {
|
|
|
20
22
|
readonly onRefreshSkipped?: (reason: RefreshReason, tokens: Tokens) => void;
|
|
21
23
|
readonly onRefreshError?: (reason: RefreshReason, error: unknown, info: {
|
|
22
24
|
transient: boolean;
|
|
25
|
+
serverError: boolean;
|
|
23
26
|
retriesLeft: number;
|
|
24
27
|
willRetry: boolean;
|
|
25
28
|
}) => void;
|
package/dist/index.js
CHANGED
|
@@ -495,6 +495,11 @@ function attachDegradedRecovery(options) {
|
|
|
495
495
|
const outcome = await options.probe();
|
|
496
496
|
if (detached || options.signal.current.kind !== "degraded") return;
|
|
497
497
|
if (outcome === "failed") {
|
|
498
|
+
if (options.isReachable?.()) {
|
|
499
|
+
options.onHold?.("probe failed but a channel is up — holding");
|
|
500
|
+
arm();
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
498
503
|
options.onOffline?.("degraded: endpoint unreachable");
|
|
499
504
|
options.kernel.dispatch({
|
|
500
505
|
type: "PROBE_FAILED_ALL",
|
|
@@ -518,14 +523,28 @@ function attachDegradedRecovery(options) {
|
|
|
518
523
|
//#endregion
|
|
519
524
|
//#region src/effects/networkChange.ts
|
|
520
525
|
function attachNetworkChange(options) {
|
|
526
|
+
const debounceMs = options.debounceMs ?? 0;
|
|
521
527
|
let detached = false;
|
|
528
|
+
let timer = null;
|
|
522
529
|
const handler = (event) => {
|
|
523
530
|
if (detached) return;
|
|
524
|
-
|
|
531
|
+
if (debounceMs <= 0) {
|
|
532
|
+
options.onChange(options.kernel, event);
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
if (timer !== null) clearTimeout(timer);
|
|
536
|
+
timer = setTimeout(() => {
|
|
537
|
+
timer = null;
|
|
538
|
+
if (!detached) options.onChange(options.kernel, event);
|
|
539
|
+
}, debounceMs);
|
|
525
540
|
};
|
|
526
541
|
options.source.addEventListener("change", handler);
|
|
527
542
|
return () => {
|
|
528
543
|
detached = true;
|
|
544
|
+
if (timer !== null) {
|
|
545
|
+
clearTimeout(timer);
|
|
546
|
+
timer = null;
|
|
547
|
+
}
|
|
529
548
|
options.source.removeEventListener("change", handler);
|
|
530
549
|
};
|
|
531
550
|
}
|
|
@@ -817,9 +836,11 @@ var DEFAULT_TRANSIENT_RETRY_DELAY_MS = 2e3;
|
|
|
817
836
|
var MAX_TIMER_DELAY_MS = 2 ** 31 - 1;
|
|
818
837
|
function attachTokenLifecycle(options) {
|
|
819
838
|
const graceMs = options.graceMs ?? DEFAULT_GRACE_MS;
|
|
820
|
-
const isTransient = options.isTransientError ?? (() => false);
|
|
821
839
|
const maxTransientRetries = options.maxTransientRetries ?? DEFAULT_MAX_TRANSIENT_RETRIES;
|
|
840
|
+
const maxServerErrorRetries = options.maxServerErrorRetries ?? maxTransientRetries;
|
|
822
841
|
const transientRetryDelayMs = options.transientRetryDelayMs ?? DEFAULT_TRANSIENT_RETRY_DELAY_MS;
|
|
842
|
+
const isTransient = options.isTransientError ?? (() => false);
|
|
843
|
+
const isServerError = options.isServerError ?? (() => false);
|
|
823
844
|
const now = options.now ?? (() => Date.now());
|
|
824
845
|
const setTimer = options.setTimer ?? ((cb, ms) => setTimeout(cb, ms));
|
|
825
846
|
const clearTimer = options.clearTimer ?? ((h) => clearTimeout(h));
|
|
@@ -907,11 +928,14 @@ function attachTokenLifecycle(options) {
|
|
|
907
928
|
} catch (err) {
|
|
908
929
|
if (detached) return;
|
|
909
930
|
const transient = isTransient(err);
|
|
910
|
-
|
|
931
|
+
const serverError = transient && isServerError(err);
|
|
932
|
+
const budget = serverError ? maxServerErrorRetries : maxTransientRetries;
|
|
933
|
+
if (transient && transientRetries < budget) {
|
|
911
934
|
transientRetries++;
|
|
912
|
-
const retriesLeft =
|
|
935
|
+
const retriesLeft = budget - transientRetries;
|
|
913
936
|
options.onRefreshError?.(reason, err, {
|
|
914
937
|
transient: true,
|
|
938
|
+
serverError,
|
|
915
939
|
retriesLeft,
|
|
916
940
|
willRetry: true
|
|
917
941
|
});
|
|
@@ -924,6 +948,7 @@ function attachTokenLifecycle(options) {
|
|
|
924
948
|
transientRetries = 0;
|
|
925
949
|
options.onRefreshError?.(reason, err, {
|
|
926
950
|
transient,
|
|
951
|
+
serverError,
|
|
927
952
|
retriesLeft: 0,
|
|
928
953
|
willRetry: false
|
|
929
954
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camera.ui/transport",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.24",
|
|
4
4
|
"description": "camera.ui transport layer — framework-agnostic connection kernel, reducer state, pluggable transports (HTTP/WS/Socket.IO/NATS), lifecycle effects and worker bridge",
|
|
5
5
|
"author": "seydx (https://github.com/cameraui/clients)",
|
|
6
6
|
"type": "module",
|