@ttt-productions/media-viewer 0.8.0 → 0.8.1
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/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/media-viewer.d.ts.map +1 -1
- package/dist/react/media-viewer.js +81 -8
- package/dist/react/media-viewer.js.map +1 -1
- package/dist/react/use-media-recovery.d.ts +42 -0
- package/dist/react/use-media-recovery.d.ts.map +1 -0
- package/dist/react/use-media-recovery.js +319 -0
- package/dist/react/use-media-recovery.js.map +1 -0
- package/dist/recovery.d.ts +94 -0
- package/dist/recovery.d.ts.map +1 -0
- package/dist/recovery.js +37 -0
- package/dist/recovery.js.map +1 -0
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export type { MediaViewerType, FallbackMode, BaseMediaProps, ImageViewerProps, VideoViewerProps, AudioViewerProps, MediaPreviewProps, MediaViewerProps, } from "./types.js";
|
|
1
|
+
export type { MediaViewerType, FallbackMode, BaseMediaProps, ImageViewerProps, VideoViewerProps, AudioViewerProps, MediaPreviewProps, MediaViewerProps, MediaDiagnosticAdapter, AssetStatusHint, DiagnosisResult, RecoveryState, } from "./types.js";
|
|
2
|
+
export { BACKOFF_SCHEDULE_MS, MAX_RECOVERY_DURATION_MS, applyJitter, backoffForAttempt, withinBudget, } from "./recovery.js";
|
|
2
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,eAAe,EACf,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,eAAe,EACf,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,sBAAsB,EACtB,eAAe,EACf,eAAe,EACf,aAAa,GACd,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,WAAW,EACX,iBAAiB,EACjB,YAAY,GACb,MAAM,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {};
|
|
1
|
+
export { BACKOFF_SCHEDULE_MS, MAX_RECOVERY_DURATION_MS, applyJitter, backoffForAttempt, withinBudget, } from "./recovery.js";
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,WAAW,EACX,iBAAiB,EACjB,YAAY,GACb,MAAM,eAAe,CAAC"}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -3,4 +3,6 @@ export { ImageViewer } from "./image-viewer.js";
|
|
|
3
3
|
export { VideoViewer } from "./video-viewer.js";
|
|
4
4
|
export { AudioViewer } from "./audio-viewer.js";
|
|
5
5
|
export { MediaFallbackLink, shouldShowFallback, EmptyFallback, ErrorFallback } from "./fallback.js";
|
|
6
|
+
export { useMediaRecovery } from "./use-media-recovery.js";
|
|
7
|
+
export type { UseMediaRecoveryOptions, UseMediaRecoveryResult } from "./use-media-recovery.js";
|
|
6
8
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACpG,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC"}
|
package/dist/react/index.js
CHANGED
|
@@ -4,4 +4,5 @@ export { ImageViewer } from "./image-viewer.js";
|
|
|
4
4
|
export { VideoViewer } from "./video-viewer.js";
|
|
5
5
|
export { AudioViewer } from "./audio-viewer.js";
|
|
6
6
|
export { MediaFallbackLink, shouldShowFallback, EmptyFallback, ErrorFallback } from "./fallback.js";
|
|
7
|
+
export { useMediaRecovery } from "./use-media-recovery.js";
|
|
7
8
|
//# sourceMappingURL=index.js.map
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACpG,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-viewer.d.ts","sourceRoot":"","sources":["../../src/react/media-viewer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,KAAK,EAAE,iBAAiB,EAAiC,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"media-viewer.d.ts","sourceRoot":"","sources":["../../src/react/media-viewer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,KAAK,EAAE,iBAAiB,EAAiC,MAAM,aAAa,CAAC;AA6GpF,wBAAgB,WAAW,CAAC,KAAK,EAAE,iBAAiB,4BAiNnD;AAED,wBAAwB;AACxB,eAAO,MAAM,YAAY,oBAAc,CAAC"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { getSimplifiedMediaType } from "@ttt-productions/media-schemas";
|
|
4
4
|
import { MediaFallbackLink, shouldShowFallback, EmptyFallback, ErrorFallback } from "./fallback.js";
|
|
5
5
|
import { ImageViewer } from "./image-viewer.js";
|
|
6
6
|
import { VideoViewer } from "./video-viewer.js";
|
|
7
7
|
import { AudioViewer } from "./audio-viewer.js";
|
|
8
|
+
import { useMediaRecovery } from "./use-media-recovery.js";
|
|
8
9
|
function inferType(props) {
|
|
9
10
|
if (props.type) {
|
|
10
11
|
// Handle MIME strings like "image/png" -> "image"
|
|
@@ -20,10 +21,61 @@ function inferType(props) {
|
|
|
20
21
|
const simplified = getSimplifiedMediaType(hint);
|
|
21
22
|
return simplified;
|
|
22
23
|
}
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Recovery-phase UI helpers
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
function RecoveryOverlay({ state, onManualRetry, isCircular }) {
|
|
28
|
+
const wrapStyle = {
|
|
29
|
+
display: "flex",
|
|
30
|
+
flexDirection: "column",
|
|
31
|
+
alignItems: "center",
|
|
32
|
+
justifyContent: "center",
|
|
33
|
+
width: "100%",
|
|
34
|
+
height: "100%",
|
|
35
|
+
gap: "0.5rem",
|
|
36
|
+
...(isCircular ? { borderRadius: "50%" } : {}),
|
|
37
|
+
};
|
|
38
|
+
if (state.phase === "processing") {
|
|
39
|
+
return (_jsx("div", { className: "mv-recovery-processing", style: wrapStyle, children: _jsx("span", { className: "mv-recovery-label", children: "Processing media\u2026" }) }));
|
|
40
|
+
}
|
|
41
|
+
if (state.phase === "finalizing") {
|
|
42
|
+
return (_jsx("div", { className: "mv-recovery-finalizing", style: wrapStyle, children: _jsx("span", { className: "mv-recovery-label", children: "Finishing media\u2026" }) }));
|
|
43
|
+
}
|
|
44
|
+
if (state.phase === "transient-retry" || state.phase === "propagating") {
|
|
45
|
+
return (_jsx("div", { className: "mv-recovery-retrying", style: wrapStyle, children: _jsx("span", { className: "mv-recovery-label", children: "Loading\u2026" }) }));
|
|
46
|
+
}
|
|
47
|
+
if (state.phase === "auth-retry") {
|
|
48
|
+
return (_jsx("div", { className: "mv-recovery-auth", style: wrapStyle, children: _jsx("span", { className: "mv-recovery-label", children: "Refreshing access\u2026" }) }));
|
|
49
|
+
}
|
|
50
|
+
if (state.phase === "hard-unavailable") {
|
|
51
|
+
return (_jsxs("div", { className: "mv-recovery-hard", style: wrapStyle, children: [_jsx(ErrorFallback, { isCircular: isCircular }), state.reason === "auth" ? (_jsx("span", { className: "mv-recovery-label", children: "Access denied." })) : (_jsx("span", { className: "mv-recovery-label", children: "Media unavailable." }))] }));
|
|
52
|
+
}
|
|
53
|
+
if (state.phase === "max-wait-fallback") {
|
|
54
|
+
return (_jsxs("div", { className: "mv-recovery-max-wait", style: wrapStyle, children: [_jsx("span", { className: "mv-recovery-label", children: "This media is taking longer than expected." }), _jsx("button", { type: "button", className: "mv-recovery-retry-btn", onClick: onManualRetry, children: "Retry" })] }));
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// Main component
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
23
61
|
export function MediaViewer(props) {
|
|
24
|
-
const { url, alt, type, className, unloadOnExit, isCircular = false, priority, skeleton, lazy, controls, autoPlay, muted, loop, posterUrl, preload, autoPlayOnVisible, onLoad, onError, onLoadChange, fallbackMode = "link", fallbackLabel, filename, mime, name, } = props;
|
|
62
|
+
const { url, alt, type, className, unloadOnExit, isCircular = false, priority, skeleton, lazy, controls, autoPlay, muted, loop, posterUrl, preload, autoPlayOnVisible, onLoad, onError, onLoadChange, fallbackMode = "link", fallbackLabel, filename, mime, name, recoveryAdapter, assetStatusHint, } = props;
|
|
25
63
|
const [hasError, setHasError] = React.useState(false);
|
|
26
64
|
const [sourceUrl, setSourceUrl] = React.useState(null);
|
|
65
|
+
// Remount key: incrementing this causes child media elements to unmount and
|
|
66
|
+
// re-mount from scratch, re-issuing the HTTP request.
|
|
67
|
+
const [remountKey, setRemountKey] = React.useState(0);
|
|
68
|
+
const handleRemount = React.useCallback(() => {
|
|
69
|
+
setHasError(false);
|
|
70
|
+
setRemountKey((k) => k + 1);
|
|
71
|
+
}, []);
|
|
72
|
+
// Recovery hook (no-op when recoveryAdapter is omitted)
|
|
73
|
+
const { recoveryState, onMediaError, onMediaLoad, manualRetry } = useMediaRecovery({
|
|
74
|
+
url: typeof url === "string" ? url : null,
|
|
75
|
+
adapter: recoveryAdapter,
|
|
76
|
+
statusHint: assetStatusHint,
|
|
77
|
+
onRemount: handleRemount,
|
|
78
|
+
});
|
|
27
79
|
React.useEffect(() => {
|
|
28
80
|
setHasError(false);
|
|
29
81
|
let objectUrl = null;
|
|
@@ -45,7 +97,19 @@ export function MediaViewer(props) {
|
|
|
45
97
|
const handleError = React.useCallback(() => {
|
|
46
98
|
setHasError(true);
|
|
47
99
|
onError?.();
|
|
48
|
-
|
|
100
|
+
// If a recovery adapter is provided, delegate to the recovery hook.
|
|
101
|
+
if (recoveryAdapter) {
|
|
102
|
+
onMediaError();
|
|
103
|
+
}
|
|
104
|
+
}, [onError, recoveryAdapter, onMediaError]);
|
|
105
|
+
const handleLoad = React.useCallback(() => {
|
|
106
|
+
setHasError(false);
|
|
107
|
+
onLoad?.();
|
|
108
|
+
// Signal successful load to the recovery state machine.
|
|
109
|
+
if (recoveryAdapter) {
|
|
110
|
+
onMediaLoad();
|
|
111
|
+
}
|
|
112
|
+
}, [onLoad, recoveryAdapter, onMediaLoad]);
|
|
49
113
|
const circularStyle = isCircular
|
|
50
114
|
? { borderRadius: "50%", overflow: "hidden" }
|
|
51
115
|
: {};
|
|
@@ -55,23 +119,32 @@ export function MediaViewer(props) {
|
|
|
55
119
|
height: "100%",
|
|
56
120
|
...circularStyle,
|
|
57
121
|
};
|
|
122
|
+
// When recovery is active and in a terminal/blocking state, show the overlay.
|
|
123
|
+
const isInRecovery = recoveryAdapter !== undefined &&
|
|
124
|
+
recoveryState.phase !== "idle" &&
|
|
125
|
+
recoveryState.phase !== "loaded" &&
|
|
126
|
+
recoveryState.phase !== "loading";
|
|
58
127
|
// No URL -> empty fallback
|
|
59
128
|
if (!sourceUrl) {
|
|
60
129
|
return (_jsx("div", { className: className, style: wrapperStyle, children: _jsx(EmptyFallback, { isCircular: isCircular }) }));
|
|
61
130
|
}
|
|
62
|
-
//
|
|
63
|
-
if (hasError) {
|
|
131
|
+
// Recovery overlay (for all non-idle/non-loaded recovery states)
|
|
132
|
+
if (isInRecovery && hasError) {
|
|
133
|
+
return (_jsx("div", { className: className, style: wrapperStyle, children: _jsx(RecoveryOverlay, { state: recoveryState, onManualRetry: manualRetry, isCircular: isCircular }) }));
|
|
134
|
+
}
|
|
135
|
+
// Vanilla error (no recovery adapter) -> error fallback
|
|
136
|
+
if (hasError && !recoveryAdapter) {
|
|
64
137
|
return (_jsx("div", { className: className, style: wrapperStyle, children: _jsx(ErrorFallback, { isCircular: isCircular }) }));
|
|
65
138
|
}
|
|
66
139
|
const t = inferType({ type, mime, name, url: sourceUrl });
|
|
67
140
|
if (t === "image") {
|
|
68
|
-
return (_jsx("div", { className: className, style: wrapperStyle, children: _jsx(ImageViewer, { url: sourceUrl, alt: alt, lazy: lazy, priority: priority, skeleton: skeleton, unloadOnExit: unloadOnExit, isCircular: isCircular, onLoad:
|
|
141
|
+
return (_jsx("div", { className: className, style: wrapperStyle, children: _jsx(ImageViewer, { url: sourceUrl, alt: alt, lazy: lazy, priority: priority, skeleton: skeleton, unloadOnExit: unloadOnExit, isCircular: isCircular, onLoad: handleLoad, onError: handleError, fallback: _jsx(ErrorFallback, { isCircular: isCircular }) }, remountKey) }));
|
|
69
142
|
}
|
|
70
143
|
if (t === "video") {
|
|
71
|
-
return (_jsx("div", { className: className, style: wrapperStyle, children: _jsx(VideoViewer, { url: sourceUrl, lazy: lazy, priority: priority, skeleton: skeleton, unloadOnExit: unloadOnExit, controls: controls, autoPlay: autoPlay, muted: muted, loop: loop, posterUrl: posterUrl, preload: preload, autoPlayOnVisible: autoPlayOnVisible, onLoad:
|
|
144
|
+
return (_jsx("div", { className: className, style: wrapperStyle, children: _jsx(VideoViewer, { url: sourceUrl, lazy: lazy, priority: priority, skeleton: skeleton, unloadOnExit: unloadOnExit, controls: controls, autoPlay: autoPlay, muted: muted, loop: loop, posterUrl: posterUrl, preload: preload, autoPlayOnVisible: autoPlayOnVisible, onLoad: handleLoad, onError: handleError }, remountKey) }));
|
|
72
145
|
}
|
|
73
146
|
if (t === "audio") {
|
|
74
|
-
return (_jsx("div", { className: className, style: wrapperStyle, children: _jsx(AudioViewer, { url: sourceUrl, lazy: lazy, priority: priority, skeleton: skeleton, controls: controls, autoPlay: autoPlay, loop: loop, preload: preload, onLoad:
|
|
147
|
+
return (_jsx("div", { className: className, style: wrapperStyle, children: _jsx(AudioViewer, { url: sourceUrl, lazy: lazy, priority: priority, skeleton: skeleton, controls: controls, autoPlay: autoPlay, loop: loop, preload: preload, onLoad: handleLoad, onLoadChange: onLoadChange, onError: handleError }, remountKey) }));
|
|
75
148
|
}
|
|
76
149
|
// Unknown type -> fallback link
|
|
77
150
|
return shouldShowFallback(fallbackMode) ? (_jsx("div", { className: className, style: wrapperStyle, children: _jsx(MediaFallbackLink, { url: sourceUrl, filename: filename, label: fallbackLabel ?? "Download" }) })) : null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-viewer.js","sourceRoot":"","sources":["../../src/react/media-viewer.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAIxE,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACpG,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"media-viewer.js","sourceRoot":"","sources":["../../src/react/media-viewer.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAIxE,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACpG,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAG3D,SAAS,SAAS,CAAC,KAAuF;IACxG,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QACf,kDAAkD;QAClD,MAAM,OAAO,GAAG,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;QACvF,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO,KAAK,OAAO;YAAE,OAAO,OAAO,CAAC;QACtF,IAAI,OAAO,KAAK,OAAO;YAAE,OAAO,OAAO,CAAC;IAC1C,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,GAAG,CAAC;IACnD,IAAI,CAAC,IAAI;QAAE,OAAO,OAAO,CAAC;IAC1B,MAAM,UAAU,GAAwB,sBAAsB,CAAC,IAAI,CAAC,CAAC;IACrE,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,8EAA8E;AAC9E,4BAA4B;AAC5B,8EAA8E;AAE9E,SAAS,eAAe,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,UAAU,EAI1D;IACC,MAAM,SAAS,GAAwB;QACrC,OAAO,EAAE,MAAM;QACf,aAAa,EAAE,QAAQ;QACvB,UAAU,EAAE,QAAQ;QACpB,cAAc,EAAE,QAAQ;QACxB,KAAK,EAAE,MAAM;QACb,MAAM,EAAE,MAAM;QACd,GAAG,EAAE,QAAQ;QACb,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC/C,CAAC;IAEF,IAAI,KAAK,CAAC,KAAK,KAAK,YAAY,EAAE,CAAC;QACjC,OAAO,CACL,cAAK,SAAS,EAAC,wBAAwB,EAAC,KAAK,EAAE,SAAS,YACtD,eAAM,SAAS,EAAC,mBAAmB,uCAAgC,GAC/D,CACP,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,CAAC,KAAK,KAAK,YAAY,EAAE,CAAC;QACjC,OAAO,CACL,cAAK,SAAS,EAAC,wBAAwB,EAAC,KAAK,EAAE,SAAS,YACtD,eAAM,SAAS,EAAC,mBAAmB,sCAA+B,GAC9D,CACP,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,CAAC,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC,KAAK,KAAK,aAAa,EAAE,CAAC;QACvE,OAAO,CACL,cAAK,SAAS,EAAC,sBAAsB,EAAC,KAAK,EAAE,SAAS,YACpD,eAAM,SAAS,EAAC,mBAAmB,8BAAuB,GACtD,CACP,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,CAAC,KAAK,KAAK,YAAY,EAAE,CAAC;QACjC,OAAO,CACL,cAAK,SAAS,EAAC,kBAAkB,EAAC,KAAK,EAAE,SAAS,YAChD,eAAM,SAAS,EAAC,mBAAmB,wCAAiC,GAChE,CACP,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,CAAC,KAAK,KAAK,kBAAkB,EAAE,CAAC;QACvC,OAAO,CACL,eAAK,SAAS,EAAC,kBAAkB,EAAC,KAAK,EAAE,SAAS,aAChD,KAAC,aAAa,IAAC,UAAU,EAAE,UAAU,GAAI,EACxC,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,CACzB,eAAM,SAAS,EAAC,mBAAmB,+BAAsB,CAC1D,CAAC,CAAC,CAAC,CACF,eAAM,SAAS,EAAC,mBAAmB,mCAA0B,CAC9D,IACG,CACP,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,CAAC,KAAK,KAAK,mBAAmB,EAAE,CAAC;QACxC,OAAO,CACL,eAAK,SAAS,EAAC,sBAAsB,EAAC,KAAK,EAAE,SAAS,aACpD,eAAM,SAAS,EAAC,mBAAmB,2DAAkD,EACrF,iBACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,uBAAuB,EACjC,OAAO,EAAE,aAAa,sBAGf,IACL,CACP,CAAC;IACJ,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E,MAAM,UAAU,WAAW,CAAC,KAAwB;IAClD,MAAM,EACJ,GAAG,EACH,GAAG,EACH,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,UAAU,GAAG,KAAK,EAClB,QAAQ,EACR,QAAQ,EACR,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,KAAK,EACL,IAAI,EACJ,SAAS,EACT,OAAO,EACP,iBAAiB,EACjB,MAAM,EACN,OAAO,EACP,YAAY,EACZ,YAAY,GAAG,MAAM,EACrB,aAAa,EACb,QAAQ,EACR,IAAI,EACJ,IAAI,EACJ,eAAe,EACf,eAAe,GAChB,GAAG,KAAK,CAAC;IAEV,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACtD,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAEtE,4EAA4E;IAC5E,sDAAsD;IACtD,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAEtD,MAAM,aAAa,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE;QAC3C,WAAW,CAAC,KAAK,CAAC,CAAC;QACnB,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9B,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,wDAAwD;IACxD,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,GAC7D,gBAAgB,CAAC;QACf,GAAG,EAAE,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI;QACzC,OAAO,EAAE,eAAe;QACxB,UAAU,EAAE,eAAe;QAC3B,SAAS,EAAE,aAAa;KACzB,CAAC,CAAC;IAEL,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,WAAW,CAAC,KAAK,CAAC,CAAC;QACnB,IAAI,SAAS,GAAkB,IAAI,CAAC;QAEpC,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC5B,YAAY,CAAC,GAAG,CAAC,CAAC;QACpB,CAAC;aAAM,IAAI,GAAG,YAAY,IAAI,EAAE,CAAC;YAC/B,SAAS,GAAG,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;YACrC,YAAY,CAAC,SAAS,CAAC,CAAC;QAC1B,CAAC;aAAM,CAAC;YACN,YAAY,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;QAED,OAAO,GAAG,EAAE;YACV,IAAI,SAAS;gBAAE,GAAG,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;QAChD,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAEV,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE;QACzC,WAAW,CAAC,IAAI,CAAC,CAAC;QAClB,OAAO,EAAE,EAAE,CAAC;QACZ,oEAAoE;QACpE,IAAI,eAAe,EAAE,CAAC;YACpB,YAAY,EAAE,CAAC;QACjB,CAAC;IACH,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,EAAE,YAAY,CAAC,CAAC,CAAC;IAE7C,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE;QACxC,WAAW,CAAC,KAAK,CAAC,CAAC;QACnB,MAAM,EAAE,EAAE,CAAC;QACX,wDAAwD;QACxD,IAAI,eAAe,EAAE,CAAC;YACpB,WAAW,EAAE,CAAC;QAChB,CAAC;IACH,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,EAAE,WAAW,CAAC,CAAC,CAAC;IAE3C,MAAM,aAAa,GAAwB,UAAU;QACnD,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE;QAC7C,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,YAAY,GAAwB;QACxC,QAAQ,EAAE,UAAU;QACpB,KAAK,EAAE,MAAM;QACb,MAAM,EAAE,MAAM;QACd,GAAG,aAAa;KACjB,CAAC;IAEF,8EAA8E;IAC9E,MAAM,YAAY,GAChB,eAAe,KAAK,SAAS;QAC7B,aAAa,CAAC,KAAK,KAAK,MAAM;QAC9B,aAAa,CAAC,KAAK,KAAK,QAAQ;QAChC,aAAa,CAAC,KAAK,KAAK,SAAS,CAAC;IAEpC,2BAA2B;IAC3B,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,CACL,cAAK,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,YAC5C,KAAC,aAAa,IAAC,UAAU,EAAE,UAAU,GAAI,GACrC,CACP,CAAC;IACJ,CAAC;IAED,iEAAiE;IACjE,IAAI,YAAY,IAAI,QAAQ,EAAE,CAAC;QAC7B,OAAO,CACL,cAAK,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,YAC5C,KAAC,eAAe,IACd,KAAK,EAAE,aAAa,EACpB,aAAa,EAAE,WAAW,EAC1B,UAAU,EAAE,UAAU,GACtB,GACE,CACP,CAAC;IACJ,CAAC;IAED,wDAAwD;IACxD,IAAI,QAAQ,IAAI,CAAC,eAAe,EAAE,CAAC;QACjC,OAAO,CACL,cAAK,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,YAC5C,KAAC,aAAa,IAAC,UAAU,EAAE,UAAU,GAAI,GACrC,CACP,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,GAAG,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC;IAE1D,IAAI,CAAC,KAAK,OAAO,EAAE,CAAC;QAClB,OAAO,CACL,cAAK,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,YAC5C,KAAC,WAAW,IAEV,GAAG,EAAE,SAAS,EACd,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,KAAC,aAAa,IAAC,UAAU,EAAE,UAAU,GAAI,IAV9C,UAAU,CAWf,GACE,CACP,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,KAAK,OAAO,EAAE,CAAC;QAClB,OAAO,CACL,cAAK,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,YAC5C,KAAC,WAAW,IAEV,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,YAAY,EAC1B,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,IAAI,EACV,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,OAAO,EAChB,iBAAiB,EAAE,iBAAiB,EACpC,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,WAAW,IAdf,UAAU,CAef,GACE,CACP,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,KAAK,OAAO,EAAE,CAAC;QAClB,OAAO,CACL,cAAK,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,YAC5C,KAAC,WAAW,IAEV,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,UAAU,EAClB,YAAY,EAAE,YAAY,EAC1B,OAAO,EAAE,WAAW,IAXf,UAAU,CAYf,GACE,CACP,CAAC;IACJ,CAAC;IAED,gCAAgC;IAChC,OAAO,kBAAkB,CAAC,YAA4B,CAAC,CAAC,CAAC,CAAC,CACxD,cAAK,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,YAC5C,KAAC,iBAAiB,IAAC,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,IAAI,UAAU,GAAI,GACzF,CACP,CAAC,CAAC,CAAC,IAAI,CAAC;AACX,CAAC;AAED,wBAAwB;AACxB,MAAM,CAAC,MAAM,YAAY,GAAG,WAAW,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { MediaDiagnosticAdapter, RecoveryState, AssetStatusHint } from "../recovery.js";
|
|
2
|
+
export interface UseMediaRecoveryOptions {
|
|
3
|
+
/** The URL of the media element. Recovery is skipped when falsy. */
|
|
4
|
+
url: string | null | undefined;
|
|
5
|
+
/** Injected diagnostic adapter from the app. */
|
|
6
|
+
adapter?: MediaDiagnosticAdapter;
|
|
7
|
+
/**
|
|
8
|
+
* Optional app hint about the server-side lifecycle of the asset.
|
|
9
|
+
* When "failed" or "rejected" the element error is treated as hard.
|
|
10
|
+
* When "processing" or "finalizing" a loading skeleton/spinner is shown
|
|
11
|
+
* without triggering the recovery probe.
|
|
12
|
+
*/
|
|
13
|
+
statusHint?: AssetStatusHint;
|
|
14
|
+
/**
|
|
15
|
+
* Called when the recovery state machine wants to remount/reload the
|
|
16
|
+
* media element. The parent should increment a remount key or equivalent
|
|
17
|
+
* so the element re-attempts loading from scratch.
|
|
18
|
+
*/
|
|
19
|
+
onRemount?: () => void;
|
|
20
|
+
}
|
|
21
|
+
export interface UseMediaRecoveryResult {
|
|
22
|
+
/** Current recovery phase. */
|
|
23
|
+
recoveryState: RecoveryState;
|
|
24
|
+
/**
|
|
25
|
+
* Call this when the underlying media element fires an error event.
|
|
26
|
+
* If no adapter is provided, or statusHint is "failed"/"rejected", the
|
|
27
|
+
* error is treated as hard immediately.
|
|
28
|
+
*/
|
|
29
|
+
onMediaError: () => void;
|
|
30
|
+
/**
|
|
31
|
+
* Call this when the underlying media element fires its successful
|
|
32
|
+
* load/decode event (onLoad / onLoadedData / onCanPlay).
|
|
33
|
+
*/
|
|
34
|
+
onMediaLoad: () => void;
|
|
35
|
+
/**
|
|
36
|
+
* Manual retry: resets the state machine back to loading and triggers a
|
|
37
|
+
* fresh remount. Usable from a "Retry" button in max-wait-fallback state.
|
|
38
|
+
*/
|
|
39
|
+
manualRetry: () => void;
|
|
40
|
+
}
|
|
41
|
+
export declare function useMediaRecovery({ url, adapter, statusHint, onRemount, }: UseMediaRecoveryOptions): UseMediaRecoveryResult;
|
|
42
|
+
//# sourceMappingURL=use-media-recovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-media-recovery.d.ts","sourceRoot":"","sources":["../../src/react/use-media-recovery.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,sBAAsB,EACtB,aAAa,EACb,eAAe,EAChB,MAAM,gBAAgB,CAAC;AA+CxB,MAAM,WAAW,uBAAuB;IACtC,oEAAoE;IACpE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAE/B,gDAAgD;IAChD,OAAO,CAAC,EAAE,sBAAsB,CAAC;IAEjC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,eAAe,CAAC;IAE7B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,sBAAsB;IACrC,8BAA8B;IAC9B,aAAa,EAAE,aAAa,CAAC;IAE7B;;;;OAIG;IACH,YAAY,EAAE,MAAM,IAAI,CAAC;IAEzB;;;OAGG;IACH,WAAW,EAAE,MAAM,IAAI,CAAC;IAExB;;;OAGG;IACH,WAAW,EAAE,MAAM,IAAI,CAAC;CACzB;AAOD,wBAAgB,gBAAgB,CAAC,EAC/B,GAAG,EACH,OAAO,EACP,UAAU,EACV,SAAS,GACV,EAAE,uBAAuB,GAAG,sBAAsB,CAmUlD"}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { backoffForAttempt, withinBudget, } from "../recovery.js";
|
|
4
|
+
const activeRecoveries = new Map();
|
|
5
|
+
/** Returns true if this is the leader (first registrant for the URL). */
|
|
6
|
+
function registerForUrl(url, fn) {
|
|
7
|
+
const existing = activeRecoveries.get(url);
|
|
8
|
+
if (existing) {
|
|
9
|
+
existing.add(fn);
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
const s = new Set();
|
|
13
|
+
s.add(fn);
|
|
14
|
+
activeRecoveries.set(url, s);
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
function unregisterFromUrl(url, fn) {
|
|
18
|
+
const s = activeRecoveries.get(url);
|
|
19
|
+
if (!s)
|
|
20
|
+
return;
|
|
21
|
+
s.delete(fn);
|
|
22
|
+
if (s.size === 0)
|
|
23
|
+
activeRecoveries.delete(url);
|
|
24
|
+
}
|
|
25
|
+
function broadcastForUrl(url, state) {
|
|
26
|
+
const s = activeRecoveries.get(url);
|
|
27
|
+
if (!s)
|
|
28
|
+
return;
|
|
29
|
+
for (const fn of s)
|
|
30
|
+
fn(state);
|
|
31
|
+
}
|
|
32
|
+
function isDocumentVisible() {
|
|
33
|
+
if (typeof document === "undefined")
|
|
34
|
+
return true;
|
|
35
|
+
return document.visibilityState !== "hidden";
|
|
36
|
+
}
|
|
37
|
+
export function useMediaRecovery({ url, adapter, statusHint, onRemount, }) {
|
|
38
|
+
const [recoveryState, setRecoveryState] = React.useState({
|
|
39
|
+
phase: "idle",
|
|
40
|
+
});
|
|
41
|
+
// Element viewport visibility
|
|
42
|
+
const [isElementVisible, setIsElementVisible] = React.useState(true);
|
|
43
|
+
// Document visibility
|
|
44
|
+
const [isDocVisible, setIsDocVisible] = React.useState(isDocumentVisible());
|
|
45
|
+
// Stable ref for the registered broadcast function — stays constant for the
|
|
46
|
+
// lifetime of this hook instance so unregister always matches the register.
|
|
47
|
+
const broadcastFnRef = React.useRef((state) => setRecoveryState(state));
|
|
48
|
+
// Stable refs for mutable values used in closures
|
|
49
|
+
const adapterRef = React.useRef(adapter);
|
|
50
|
+
adapterRef.current = adapter;
|
|
51
|
+
const onRemountRef = React.useRef(onRemount);
|
|
52
|
+
onRemountRef.current = onRemount;
|
|
53
|
+
const statusHintRef = React.useRef(statusHint);
|
|
54
|
+
statusHintRef.current = statusHint;
|
|
55
|
+
// Retry timer
|
|
56
|
+
const retryTimerRef = React.useRef(null);
|
|
57
|
+
// Recovery cycle bookkeeping
|
|
58
|
+
const firstFailureAtRef = React.useRef(null);
|
|
59
|
+
const authRefreshedRef = React.useRef(false);
|
|
60
|
+
const attemptRef = React.useRef(0);
|
|
61
|
+
const isLeaderRef = React.useRef(false);
|
|
62
|
+
const isRegisteredRef = React.useRef(false);
|
|
63
|
+
const stableUrl = url ?? null;
|
|
64
|
+
// -------------------------------------------------------------------------
|
|
65
|
+
// Document visibility
|
|
66
|
+
// -------------------------------------------------------------------------
|
|
67
|
+
React.useEffect(() => {
|
|
68
|
+
if (typeof document === "undefined")
|
|
69
|
+
return;
|
|
70
|
+
const handler = () => setIsDocVisible(document.visibilityState !== "hidden");
|
|
71
|
+
document.addEventListener("visibilitychange", handler);
|
|
72
|
+
return () => document.removeEventListener("visibilitychange", handler);
|
|
73
|
+
}, []);
|
|
74
|
+
// -------------------------------------------------------------------------
|
|
75
|
+
// IntersectionObserver for element visibility (container-agnostic)
|
|
76
|
+
// Uses the mounted DOM node of the component; we observe document.body
|
|
77
|
+
// as a fallback for tests that don't wire a ref.
|
|
78
|
+
// -------------------------------------------------------------------------
|
|
79
|
+
React.useEffect(() => {
|
|
80
|
+
if (typeof IntersectionObserver === "undefined")
|
|
81
|
+
return;
|
|
82
|
+
const target = document.body;
|
|
83
|
+
const io = new IntersectionObserver(([e]) => setIsElementVisible(e?.isIntersecting ?? true), { threshold: 0.01 });
|
|
84
|
+
io.observe(target);
|
|
85
|
+
return () => io.disconnect();
|
|
86
|
+
}, []);
|
|
87
|
+
// -------------------------------------------------------------------------
|
|
88
|
+
// Cleanup on URL change or unmount
|
|
89
|
+
// -------------------------------------------------------------------------
|
|
90
|
+
React.useEffect(() => {
|
|
91
|
+
// Reset when URL changes
|
|
92
|
+
if (retryTimerRef.current !== null) {
|
|
93
|
+
clearTimeout(retryTimerRef.current);
|
|
94
|
+
retryTimerRef.current = null;
|
|
95
|
+
}
|
|
96
|
+
firstFailureAtRef.current = null;
|
|
97
|
+
authRefreshedRef.current = false;
|
|
98
|
+
attemptRef.current = 0;
|
|
99
|
+
setRecoveryState({ phase: "idle" });
|
|
100
|
+
// broadcastFnRef.current is a stable ref (created once, never reassigned),
|
|
101
|
+
// so capture it for the cleanup closure rather than reading the ref there
|
|
102
|
+
// (react-hooks/exhaustive-deps flags ref reads in cleanup).
|
|
103
|
+
const broadcastFn = broadcastFnRef.current;
|
|
104
|
+
return () => {
|
|
105
|
+
// Unregister from dedup on unmount or URL change
|
|
106
|
+
if (stableUrl && isRegisteredRef.current) {
|
|
107
|
+
unregisterFromUrl(stableUrl, broadcastFn);
|
|
108
|
+
isRegisteredRef.current = false;
|
|
109
|
+
isLeaderRef.current = false;
|
|
110
|
+
}
|
|
111
|
+
if (retryTimerRef.current !== null) {
|
|
112
|
+
clearTimeout(retryTimerRef.current);
|
|
113
|
+
retryTimerRef.current = null;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}, [stableUrl]);
|
|
117
|
+
// -------------------------------------------------------------------------
|
|
118
|
+
// Resume paused retries when visibility restores
|
|
119
|
+
// -------------------------------------------------------------------------
|
|
120
|
+
React.useEffect(() => {
|
|
121
|
+
if (!isElementVisible || !isDocVisible)
|
|
122
|
+
return;
|
|
123
|
+
if (recoveryState.phase !== "transient-retry")
|
|
124
|
+
return;
|
|
125
|
+
if (retryTimerRef.current !== null)
|
|
126
|
+
return; // timer already running
|
|
127
|
+
// Timer was cleared due to hidden state — reschedule.
|
|
128
|
+
const remaining = Math.max(0, recoveryState.nextRetryMs);
|
|
129
|
+
retryTimerRef.current = setTimeout(() => {
|
|
130
|
+
retryTimerRef.current = null;
|
|
131
|
+
onRemountRef.current?.();
|
|
132
|
+
}, remaining);
|
|
133
|
+
}, [isElementVisible, isDocVisible, recoveryState]);
|
|
134
|
+
// -------------------------------------------------------------------------
|
|
135
|
+
// Visibility snapshot ref (kept current so timer callbacks read fresh values)
|
|
136
|
+
// -------------------------------------------------------------------------
|
|
137
|
+
const visibilityRef = React.useRef({ isElementVisible, isDocVisible });
|
|
138
|
+
visibilityRef.current = { isElementVisible, isDocVisible };
|
|
139
|
+
// -------------------------------------------------------------------------
|
|
140
|
+
// Core recovery probe + state advance
|
|
141
|
+
// -------------------------------------------------------------------------
|
|
142
|
+
const runRecovery = React.useCallback(async (attempt) => {
|
|
143
|
+
if (!stableUrl)
|
|
144
|
+
return;
|
|
145
|
+
const currentAdapter = adapterRef.current;
|
|
146
|
+
const hint = statusHintRef.current;
|
|
147
|
+
let diagnosis;
|
|
148
|
+
if (!currentAdapter?.probe) {
|
|
149
|
+
// No probe: treat as transient for bounded retry purposes
|
|
150
|
+
diagnosis = { kind: "transient" };
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
try {
|
|
154
|
+
diagnosis = await currentAdapter.probe(stableUrl);
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
diagnosis = { kind: "transient" };
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (diagnosis.kind === "hard") {
|
|
161
|
+
const hardState = { phase: "hard-unavailable" };
|
|
162
|
+
setRecoveryState(hardState);
|
|
163
|
+
broadcastForUrl(stableUrl, hardState);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (diagnosis.kind === "auth") {
|
|
167
|
+
if (!authRefreshedRef.current) {
|
|
168
|
+
authRefreshedRef.current = true;
|
|
169
|
+
const authState = { phase: "auth-retry" };
|
|
170
|
+
setRecoveryState(authState);
|
|
171
|
+
broadcastForUrl(stableUrl, authState);
|
|
172
|
+
try {
|
|
173
|
+
if (hint !== "failed" && hint !== "rejected") {
|
|
174
|
+
await currentAdapter?.refreshSession?.();
|
|
175
|
+
await currentAdapter?.refreshGrant?.(stableUrl);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// swallow — fall through to remount
|
|
180
|
+
}
|
|
181
|
+
onRemountRef.current?.();
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
// Already refreshed once → hard auth failure
|
|
185
|
+
const hardAuthState = {
|
|
186
|
+
phase: "hard-unavailable",
|
|
187
|
+
reason: "auth",
|
|
188
|
+
};
|
|
189
|
+
setRecoveryState(hardAuthState);
|
|
190
|
+
broadcastForUrl(stableUrl, hardAuthState);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
// Transient: schedule next retry with backoff
|
|
194
|
+
const nextDelay = backoffForAttempt(attempt);
|
|
195
|
+
const nextRetryAt = Date.now() + nextDelay;
|
|
196
|
+
if (firstFailureAtRef.current !== null &&
|
|
197
|
+
!withinBudget(firstFailureAtRef.current, nextRetryAt)) {
|
|
198
|
+
const fallbackState = { phase: "max-wait-fallback" };
|
|
199
|
+
setRecoveryState(fallbackState);
|
|
200
|
+
broadcastForUrl(stableUrl, fallbackState);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const retryState = {
|
|
204
|
+
phase: "transient-retry",
|
|
205
|
+
attempt,
|
|
206
|
+
nextRetryMs: nextDelay,
|
|
207
|
+
};
|
|
208
|
+
setRecoveryState(retryState);
|
|
209
|
+
broadcastForUrl(stableUrl, retryState);
|
|
210
|
+
// Schedule remount — cancel if hidden at fire time
|
|
211
|
+
retryTimerRef.current = setTimeout(() => {
|
|
212
|
+
retryTimerRef.current = null;
|
|
213
|
+
const { isElementVisible: elVis, isDocVisible: docVis } = visibilityRef.current;
|
|
214
|
+
if (!elVis || !docVis) {
|
|
215
|
+
// Leave state as transient-retry; visibility effect will reschedule.
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
onRemountRef.current?.();
|
|
219
|
+
}, nextDelay);
|
|
220
|
+
}, [stableUrl]);
|
|
221
|
+
// -------------------------------------------------------------------------
|
|
222
|
+
// Public: onMediaError
|
|
223
|
+
// -------------------------------------------------------------------------
|
|
224
|
+
const onMediaError = React.useCallback(() => {
|
|
225
|
+
if (!stableUrl)
|
|
226
|
+
return;
|
|
227
|
+
const hint = statusHintRef.current;
|
|
228
|
+
// Terminal app-hint states — no probe, immediate hard error
|
|
229
|
+
if (hint === "failed" || hint === "rejected") {
|
|
230
|
+
const s = { phase: "hard-unavailable", reason: hint };
|
|
231
|
+
setRecoveryState(s);
|
|
232
|
+
broadcastForUrl(stableUrl, s);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
// Processing/finalizing hints — show phase overlay, no probe
|
|
236
|
+
if (hint === "processing") {
|
|
237
|
+
const s = { phase: "processing" };
|
|
238
|
+
setRecoveryState(s);
|
|
239
|
+
broadcastForUrl(stableUrl, s);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (hint === "finalizing") {
|
|
243
|
+
const s = { phase: "finalizing" };
|
|
244
|
+
setRecoveryState(s);
|
|
245
|
+
broadcastForUrl(stableUrl, s);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
// Record first failure time
|
|
249
|
+
if (firstFailureAtRef.current === null) {
|
|
250
|
+
firstFailureAtRef.current = Date.now();
|
|
251
|
+
}
|
|
252
|
+
const attempt = attemptRef.current;
|
|
253
|
+
attemptRef.current += 1;
|
|
254
|
+
// Dedup registration
|
|
255
|
+
if (!isRegisteredRef.current) {
|
|
256
|
+
isLeaderRef.current = registerForUrl(stableUrl, broadcastFnRef.current);
|
|
257
|
+
isRegisteredRef.current = true;
|
|
258
|
+
}
|
|
259
|
+
// Only the leader drives recovery probes
|
|
260
|
+
if (!isLeaderRef.current) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
void runRecovery(attempt);
|
|
264
|
+
}, [stableUrl, runRecovery]);
|
|
265
|
+
// -------------------------------------------------------------------------
|
|
266
|
+
// Public: onMediaLoad
|
|
267
|
+
// -------------------------------------------------------------------------
|
|
268
|
+
const onMediaLoad = React.useCallback(() => {
|
|
269
|
+
if (retryTimerRef.current !== null) {
|
|
270
|
+
clearTimeout(retryTimerRef.current);
|
|
271
|
+
retryTimerRef.current = null;
|
|
272
|
+
}
|
|
273
|
+
if (stableUrl && isRegisteredRef.current) {
|
|
274
|
+
unregisterFromUrl(stableUrl, broadcastFnRef.current);
|
|
275
|
+
isRegisteredRef.current = false;
|
|
276
|
+
isLeaderRef.current = false;
|
|
277
|
+
}
|
|
278
|
+
firstFailureAtRef.current = null;
|
|
279
|
+
authRefreshedRef.current = false;
|
|
280
|
+
attemptRef.current = 0;
|
|
281
|
+
const loadedState = { phase: "loaded" };
|
|
282
|
+
setRecoveryState(loadedState);
|
|
283
|
+
if (stableUrl)
|
|
284
|
+
broadcastForUrl(stableUrl, loadedState);
|
|
285
|
+
}, [stableUrl]);
|
|
286
|
+
// -------------------------------------------------------------------------
|
|
287
|
+
// Public: manualRetry
|
|
288
|
+
// -------------------------------------------------------------------------
|
|
289
|
+
const manualRetry = React.useCallback(() => {
|
|
290
|
+
if (!stableUrl)
|
|
291
|
+
return;
|
|
292
|
+
if (retryTimerRef.current !== null) {
|
|
293
|
+
clearTimeout(retryTimerRef.current);
|
|
294
|
+
retryTimerRef.current = null;
|
|
295
|
+
}
|
|
296
|
+
// Reset cycle
|
|
297
|
+
firstFailureAtRef.current = null;
|
|
298
|
+
authRefreshedRef.current = false;
|
|
299
|
+
attemptRef.current = 0;
|
|
300
|
+
// Re-establish as leader
|
|
301
|
+
if (isRegisteredRef.current) {
|
|
302
|
+
unregisterFromUrl(stableUrl, broadcastFnRef.current);
|
|
303
|
+
isRegisteredRef.current = false;
|
|
304
|
+
}
|
|
305
|
+
isLeaderRef.current = registerForUrl(stableUrl, broadcastFnRef.current);
|
|
306
|
+
isRegisteredRef.current = true;
|
|
307
|
+
const loadingState = { phase: "loading" };
|
|
308
|
+
setRecoveryState(loadingState);
|
|
309
|
+
broadcastForUrl(stableUrl, loadingState);
|
|
310
|
+
onRemountRef.current?.();
|
|
311
|
+
}, [stableUrl]);
|
|
312
|
+
return {
|
|
313
|
+
recoveryState,
|
|
314
|
+
onMediaError,
|
|
315
|
+
onMediaLoad,
|
|
316
|
+
manualRetry,
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
//# sourceMappingURL=use-media-recovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-media-recovery.js","sourceRoot":"","sources":["../../src/react/use-media-recovery.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,EACL,iBAAiB,EACjB,YAAY,GACb,MAAM,gBAAgB,CAAC;AAWxB,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAA4B,CAAC;AAE7D,yEAAyE;AACzE,SAAS,cAAc,CAAC,GAAW,EAAE,EAAe;IAClD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC3C,IAAI,QAAQ,EAAE,CAAC;QACb,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACjB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,CAAC,GAAG,IAAI,GAAG,EAAe,CAAC;IACjC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACV,gBAAgB,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IAC7B,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,iBAAiB,CAAC,GAAW,EAAE,EAAe;IACrD,MAAM,CAAC,GAAG,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,CAAC,CAAC;QAAE,OAAO;IACf,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACb,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC;QAAE,gBAAgB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AACjD,CAAC;AAED,SAAS,eAAe,CAAC,GAAW,EAAE,KAAoB;IACxD,MAAM,CAAC,GAAG,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,CAAC,CAAC;QAAE,OAAO;IACf,KAAK,MAAM,EAAE,IAAI,CAAC;QAAE,EAAE,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC;AAqDD,SAAS,iBAAiB;IACxB,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IACjD,OAAO,QAAQ,CAAC,eAAe,KAAK,QAAQ,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,EAC/B,GAAG,EACH,OAAO,EACP,UAAU,EACV,SAAS,GACe;IACxB,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAgB;QACtE,KAAK,EAAE,MAAM;KACd,CAAC,CAAC;IAEH,8BAA8B;IAC9B,MAAM,CAAC,gBAAgB,EAAE,mBAAmB,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAErE,sBAAsB;IACtB,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAE5E,4EAA4E;IAC5E,4EAA4E;IAC5E,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,CAAc,CAAC,KAAK,EAAE,EAAE,CACzD,gBAAgB,CAAC,KAAK,CAAC,CACxB,CAAC;IAEF,kDAAkD;IAClD,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAqC,OAAO,CAAC,CAAC;IAC7E,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;IAC7B,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,CAA2B,SAAS,CAAC,CAAC;IACvE,YAAY,CAAC,OAAO,GAAG,SAAS,CAAC;IACjC,MAAM,aAAa,GAAG,KAAK,CAAC,MAAM,CAA8B,UAAU,CAAC,CAAC;IAC5E,aAAa,CAAC,OAAO,GAAG,UAAU,CAAC;IAEnC,cAAc;IACd,MAAM,aAAa,GAAG,KAAK,CAAC,MAAM,CAAuC,IAAI,CAAC,CAAC;IAE/E,6BAA6B;IAC7B,MAAM,iBAAiB,GAAG,KAAK,CAAC,MAAM,CAAgB,IAAI,CAAC,CAAC;IAC5D,MAAM,gBAAgB,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7C,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,eAAe,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAE5C,MAAM,SAAS,GAAG,GAAG,IAAI,IAAI,CAAC;IAE9B,4EAA4E;IAC5E,sBAAsB;IACtB,4EAA4E;IAC5E,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,IAAI,OAAO,QAAQ,KAAK,WAAW;YAAE,OAAO;QAC5C,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,eAAe,CAAC,QAAQ,CAAC,eAAe,KAAK,QAAQ,CAAC,CAAC;QAC7E,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,OAAO,CAAC,CAAC;QACvD,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC,kBAAkB,EAAE,OAAO,CAAC,CAAC;IACzE,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,4EAA4E;IAC5E,mEAAmE;IACnE,uEAAuE;IACvE,iDAAiD;IACjD,4EAA4E;IAC5E,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,IAAI,OAAO,oBAAoB,KAAK,WAAW;YAAE,OAAO;QACxD,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC;QAC7B,MAAM,EAAE,GAAG,IAAI,oBAAoB,CACjC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,mBAAmB,CAAC,CAAC,EAAE,cAAc,IAAI,IAAI,CAAC,EACvD,EAAE,SAAS,EAAE,IAAI,EAAE,CACpB,CAAC;QACF,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACnB,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,UAAU,EAAE,CAAC;IAC/B,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,4EAA4E;IAC5E,mCAAmC;IACnC,4EAA4E;IAC5E,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,yBAAyB;QACzB,IAAI,aAAa,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YACnC,YAAY,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YACpC,aAAa,CAAC,OAAO,GAAG,IAAI,CAAC;QAC/B,CAAC;QACD,iBAAiB,CAAC,OAAO,GAAG,IAAI,CAAC;QACjC,gBAAgB,CAAC,OAAO,GAAG,KAAK,CAAC;QACjC,UAAU,CAAC,OAAO,GAAG,CAAC,CAAC;QAEvB,gBAAgB,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QAEpC,2EAA2E;QAC3E,0EAA0E;QAC1E,4DAA4D;QAC5D,MAAM,WAAW,GAAG,cAAc,CAAC,OAAO,CAAC;QAC3C,OAAO,GAAG,EAAE;YACV,iDAAiD;YACjD,IAAI,SAAS,IAAI,eAAe,CAAC,OAAO,EAAE,CAAC;gBACzC,iBAAiB,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;gBAC1C,eAAe,CAAC,OAAO,GAAG,KAAK,CAAC;gBAChC,WAAW,CAAC,OAAO,GAAG,KAAK,CAAC;YAC9B,CAAC;YACD,IAAI,aAAa,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;gBACnC,YAAY,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;gBACpC,aAAa,CAAC,OAAO,GAAG,IAAI,CAAC;YAC/B,CAAC;QACH,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IAEhB,4EAA4E;IAC5E,iDAAiD;IACjD,4EAA4E;IAC5E,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,IAAI,CAAC,gBAAgB,IAAI,CAAC,YAAY;YAAE,OAAO;QAC/C,IAAI,aAAa,CAAC,KAAK,KAAK,iBAAiB;YAAE,OAAO;QACtD,IAAI,aAAa,CAAC,OAAO,KAAK,IAAI;YAAE,OAAO,CAAC,wBAAwB;QAEpE,sDAAsD;QACtD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;QACzD,aAAa,CAAC,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YACtC,aAAa,CAAC,OAAO,GAAG,IAAI,CAAC;YAC7B,YAAY,CAAC,OAAO,EAAE,EAAE,CAAC;QAC3B,CAAC,EAAE,SAAS,CAAC,CAAC;IAChB,CAAC,EAAE,CAAC,gBAAgB,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC;IAEpD,4EAA4E;IAC5E,8EAA8E;IAC9E,4EAA4E;IAC5E,MAAM,aAAa,GAAG,KAAK,CAAC,MAAM,CAAC,EAAE,gBAAgB,EAAE,YAAY,EAAE,CAAC,CAAC;IACvE,aAAa,CAAC,OAAO,GAAG,EAAE,gBAAgB,EAAE,YAAY,EAAE,CAAC;IAE3D,4EAA4E;IAC5E,sCAAsC;IACtC,4EAA4E;IAC5E,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,CACnC,KAAK,EAAE,OAAe,EAAE,EAAE;QACxB,IAAI,CAAC,SAAS;YAAE,OAAO;QAEvB,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,CAAC;QAC1C,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC;QAEnC,IAAI,SAAmD,CAAC;QAExD,IAAI,CAAC,cAAc,EAAE,KAAK,EAAE,CAAC;YAC3B,0DAA0D;YAC1D,SAAS,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC;gBACH,SAAS,GAAG,MAAM,cAAc,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YACpD,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;YACpC,CAAC;QACH,CAAC;QAED,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC9B,MAAM,SAAS,GAAkB,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC;YAC/D,gBAAgB,CAAC,SAAS,CAAC,CAAC;YAC5B,eAAe,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YACtC,OAAO;QACT,CAAC;QAED,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC9B,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC;gBAC9B,gBAAgB,CAAC,OAAO,GAAG,IAAI,CAAC;gBAChC,MAAM,SAAS,GAAkB,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;gBACzD,gBAAgB,CAAC,SAAS,CAAC,CAAC;gBAC5B,eAAe,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;gBAEtC,IAAI,CAAC;oBACH,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;wBAC7C,MAAM,cAAc,EAAE,cAAc,EAAE,EAAE,CAAC;wBACzC,MAAM,cAAc,EAAE,YAAY,EAAE,CAAC,SAAS,CAAC,CAAC;oBAClD,CAAC;gBACH,CAAC;gBAAC,MAAM,CAAC;oBACP,oCAAoC;gBACtC,CAAC;gBAED,YAAY,CAAC,OAAO,EAAE,EAAE,CAAC;gBACzB,OAAO;YACT,CAAC;YACD,6CAA6C;YAC7C,MAAM,aAAa,GAAkB;gBACnC,KAAK,EAAE,kBAAkB;gBACzB,MAAM,EAAE,MAAM;aACf,CAAC;YACF,gBAAgB,CAAC,aAAa,CAAC,CAAC;YAChC,eAAe,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;YAC1C,OAAO;QACT,CAAC;QAED,8CAA8C;QAC9C,MAAM,SAAS,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAC7C,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QAE3C,IACE,iBAAiB,CAAC,OAAO,KAAK,IAAI;YAClC,CAAC,YAAY,CAAC,iBAAiB,CAAC,OAAO,EAAE,WAAW,CAAC,EACrD,CAAC;YACD,MAAM,aAAa,GAAkB,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC;YACpE,gBAAgB,CAAC,aAAa,CAAC,CAAC;YAChC,eAAe,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;YAC1C,OAAO;QACT,CAAC;QAED,MAAM,UAAU,GAAkB;YAChC,KAAK,EAAE,iBAAiB;YACxB,OAAO;YACP,WAAW,EAAE,SAAS;SACvB,CAAC;QACF,gBAAgB,CAAC,UAAU,CAAC,CAAC;QAC7B,eAAe,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;QAEvC,mDAAmD;QACnD,aAAa,CAAC,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YACtC,aAAa,CAAC,OAAO,GAAG,IAAI,CAAC;YAC7B,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC;YAChF,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;gBACtB,qEAAqE;gBACrE,OAAO;YACT,CAAC;YACD,YAAY,CAAC,OAAO,EAAE,EAAE,CAAC;QAC3B,CAAC,EAAE,SAAS,CAAC,CAAC;IAChB,CAAC,EACD,CAAC,SAAS,CAAC,CACZ,CAAC;IAEF,4EAA4E;IAC5E,uBAAuB;IACvB,4EAA4E;IAC5E,MAAM,YAAY,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE;QAC1C,IAAI,CAAC,SAAS;YAAE,OAAO;QAEvB,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC;QAEnC,4DAA4D;QAC5D,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;YAC7C,MAAM,CAAC,GAAkB,EAAE,KAAK,EAAE,kBAAkB,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;YACrE,gBAAgB,CAAC,CAAC,CAAC,CAAC;YACpB,eAAe,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;YAC9B,OAAO;QACT,CAAC;QAED,6DAA6D;QAC7D,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;YAC1B,MAAM,CAAC,GAAkB,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;YACjD,gBAAgB,CAAC,CAAC,CAAC,CAAC;YACpB,eAAe,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;YAC9B,OAAO;QACT,CAAC;QACD,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;YAC1B,MAAM,CAAC,GAAkB,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;YACjD,gBAAgB,CAAC,CAAC,CAAC,CAAC;YACpB,eAAe,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;YAC9B,OAAO;QACT,CAAC;QAED,4BAA4B;QAC5B,IAAI,iBAAiB,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YACvC,iBAAiB,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzC,CAAC;QAED,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC;QACnC,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC;QAExB,qBAAqB;QACrB,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,CAAC;YAC7B,WAAW,CAAC,OAAO,GAAG,cAAc,CAAC,SAAS,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;YACxE,eAAe,CAAC,OAAO,GAAG,IAAI,CAAC;QACjC,CAAC;QAED,yCAAyC;QACzC,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;YACzB,OAAO;QACT,CAAC;QAED,KAAK,WAAW,CAAC,OAAO,CAAC,CAAC;IAC5B,CAAC,EAAE,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC;IAE7B,4EAA4E;IAC5E,sBAAsB;IACtB,4EAA4E;IAC5E,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE;QACzC,IAAI,aAAa,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YACnC,YAAY,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YACpC,aAAa,CAAC,OAAO,GAAG,IAAI,CAAC;QAC/B,CAAC;QACD,IAAI,SAAS,IAAI,eAAe,CAAC,OAAO,EAAE,CAAC;YACzC,iBAAiB,CAAC,SAAS,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;YACrD,eAAe,CAAC,OAAO,GAAG,KAAK,CAAC;YAChC,WAAW,CAAC,OAAO,GAAG,KAAK,CAAC;QAC9B,CAAC;QACD,iBAAiB,CAAC,OAAO,GAAG,IAAI,CAAC;QACjC,gBAAgB,CAAC,OAAO,GAAG,KAAK,CAAC;QACjC,UAAU,CAAC,OAAO,GAAG,CAAC,CAAC;QAEvB,MAAM,WAAW,GAAkB,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;QACvD,gBAAgB,CAAC,WAAW,CAAC,CAAC;QAC9B,IAAI,SAAS;YAAE,eAAe,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IACzD,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IAEhB,4EAA4E;IAC5E,sBAAsB;IACtB,4EAA4E;IAC5E,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE;QACzC,IAAI,CAAC,SAAS;YAAE,OAAO;QAEvB,IAAI,aAAa,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YACnC,YAAY,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YACpC,aAAa,CAAC,OAAO,GAAG,IAAI,CAAC;QAC/B,CAAC;QAED,cAAc;QACd,iBAAiB,CAAC,OAAO,GAAG,IAAI,CAAC;QACjC,gBAAgB,CAAC,OAAO,GAAG,KAAK,CAAC;QACjC,UAAU,CAAC,OAAO,GAAG,CAAC,CAAC;QAEvB,yBAAyB;QACzB,IAAI,eAAe,CAAC,OAAO,EAAE,CAAC;YAC5B,iBAAiB,CAAC,SAAS,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;YACrD,eAAe,CAAC,OAAO,GAAG,KAAK,CAAC;QAClC,CAAC;QACD,WAAW,CAAC,OAAO,GAAG,cAAc,CAAC,SAAS,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;QACxE,eAAe,CAAC,OAAO,GAAG,IAAI,CAAC;QAE/B,MAAM,YAAY,GAAkB,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;QACzD,gBAAgB,CAAC,YAAY,CAAC,CAAC;QAC/B,eAAe,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;QACzC,YAAY,CAAC,OAAO,EAAE,EAAE,CAAC;IAC3B,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IAEhB,OAAO;QACL,aAAa;QACb,YAAY;QACZ,WAAW;QACX,WAAW;KACZ,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic bounded media-recovery state machine.
|
|
3
|
+
*
|
|
4
|
+
* Owns: backoff schedule, state transition logic, dedup key shape.
|
|
5
|
+
* Does NOT own: probe implementation, session/grant refresh, telemetry.
|
|
6
|
+
* Those are injected via MediaDiagnosticAdapter.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Opaque diagnosis result returned by the app's probe(). The package treats
|
|
10
|
+
* this value OPAQUELY — it only checks the `kind` discriminant; all
|
|
11
|
+
* domain-specific interpretation (x-ttt-deny, HTTP status codes, etc.) lives
|
|
12
|
+
* in the app adapter.
|
|
13
|
+
*/
|
|
14
|
+
export type DiagnosisResult = {
|
|
15
|
+
kind: "transient";
|
|
16
|
+
} | {
|
|
17
|
+
kind: "auth";
|
|
18
|
+
} | {
|
|
19
|
+
kind: "hard";
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Optional hint from the app about the asset's known server-side lifecycle
|
|
23
|
+
* state. When provided it informs which recovery states are applicable.
|
|
24
|
+
* The package treats these opaquely — it does not interpret TTT-specific
|
|
25
|
+
* pendingMedia.publicationState fields directly.
|
|
26
|
+
*/
|
|
27
|
+
export type AssetStatusHint = "processing" | "finalizing" | "live" | "failed" | "rejected";
|
|
28
|
+
/**
|
|
29
|
+
* Injected adapter interface. The TTT app implements this; generic consumers
|
|
30
|
+
* may supply a no-op or partial adapter.
|
|
31
|
+
*
|
|
32
|
+
* Every method is optional — callers guard before invoking.
|
|
33
|
+
*/
|
|
34
|
+
export interface MediaDiagnosticAdapter {
|
|
35
|
+
/**
|
|
36
|
+
* Issue a credentialed HEAD probe against the given URL and return a
|
|
37
|
+
* diagnosis. Should resolve quickly (e.g. 5 s timeout). Must never throw
|
|
38
|
+
* — return { kind: "transient" } on network errors.
|
|
39
|
+
*/
|
|
40
|
+
probe?: (url: string) => Promise<DiagnosisResult>;
|
|
41
|
+
/**
|
|
42
|
+
* Attempt to refresh the media session (re-mint the HttpOnly cookie).
|
|
43
|
+
* Called at most once per auth-recovery cycle. Must never throw.
|
|
44
|
+
*/
|
|
45
|
+
refreshSession?: () => Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Attempt to refresh a scoped grant for the given URL.
|
|
48
|
+
* Called at most once per auth-recovery cycle. Must never throw.
|
|
49
|
+
*/
|
|
50
|
+
refreshGrant?: (url: string) => Promise<void>;
|
|
51
|
+
}
|
|
52
|
+
export type RecoveryState = {
|
|
53
|
+
phase: "idle";
|
|
54
|
+
} | {
|
|
55
|
+
phase: "loading";
|
|
56
|
+
} | {
|
|
57
|
+
phase: "processing";
|
|
58
|
+
} | {
|
|
59
|
+
phase: "finalizing";
|
|
60
|
+
} | {
|
|
61
|
+
phase: "loaded";
|
|
62
|
+
} | {
|
|
63
|
+
phase: "transient-retry";
|
|
64
|
+
attempt: number;
|
|
65
|
+
nextRetryMs: number;
|
|
66
|
+
} | {
|
|
67
|
+
phase: "propagating";
|
|
68
|
+
} | {
|
|
69
|
+
phase: "auth-retry";
|
|
70
|
+
} | {
|
|
71
|
+
phase: "hard-unavailable";
|
|
72
|
+
reason?: string;
|
|
73
|
+
} | {
|
|
74
|
+
phase: "max-wait-fallback";
|
|
75
|
+
};
|
|
76
|
+
export declare const BACKOFF_SCHEDULE_MS: readonly [2000, 5000, 10000, 20000, 30000, 30000];
|
|
77
|
+
/** Maximum total time from first failure before giving up, in ms. */
|
|
78
|
+
export declare const MAX_RECOVERY_DURATION_MS = 120000;
|
|
79
|
+
/**
|
|
80
|
+
* Apply ±20% jitter to a backoff delay. Deterministic seed not required —
|
|
81
|
+
* slight randomness prevents thundering herds across many assets.
|
|
82
|
+
*/
|
|
83
|
+
export declare function applyJitter(delayMs: number): number;
|
|
84
|
+
/**
|
|
85
|
+
* Return the backoff delay for a given attempt index (0-based).
|
|
86
|
+
* Clamps to the last schedule entry beyond the end.
|
|
87
|
+
*/
|
|
88
|
+
export declare function backoffForAttempt(attempt: number): number;
|
|
89
|
+
/**
|
|
90
|
+
* True when the total elapsed time since first failure would still be within
|
|
91
|
+
* the MAX_RECOVERY_DURATION_MS budget after the next retry.
|
|
92
|
+
*/
|
|
93
|
+
export declare function withinBudget(firstFailureAt: number, nextRetryAt: number): boolean;
|
|
94
|
+
//# sourceMappingURL=recovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../src/recovery.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErB;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GACvB,YAAY,GACZ,YAAY,GACZ,MAAM,GACN,QAAQ,GACR,UAAU,CAAC;AAEf;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IAElD;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAErC;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;AAMD,MAAM,MAAM,aAAa,GACrB;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GACjB;IAAE,KAAK,EAAE,SAAS,CAAA;CAAE,GACpB;IAAE,KAAK,EAAE,YAAY,CAAA;CAAE,GACvB;IAAE,KAAK,EAAE,YAAY,CAAA;CAAE,GACvB;IAAE,KAAK,EAAE,QAAQ,CAAA;CAAE,GACnB;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAClE;IAAE,KAAK,EAAE,aAAa,CAAA;CAAE,GACxB;IAAE,KAAK,EAAE,YAAY,CAAA;CAAE,GACvB;IAAE,KAAK,EAAE,kBAAkB,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,KAAK,EAAE,mBAAmB,CAAA;CAAE,CAAC;AAGnC,eAAO,MAAM,mBAAmB,mDAAoD,CAAC;AAErF,qEAAqE;AACrE,eAAO,MAAM,wBAAwB,SAAU,CAAC;AAKhD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAEjF"}
|
package/dist/recovery.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic bounded media-recovery state machine.
|
|
3
|
+
*
|
|
4
|
+
* Owns: backoff schedule, state transition logic, dedup key shape.
|
|
5
|
+
* Does NOT own: probe implementation, session/grant refresh, telemetry.
|
|
6
|
+
* Those are injected via MediaDiagnosticAdapter.
|
|
7
|
+
*/
|
|
8
|
+
// Backoff schedule in ms: 2s → 5s → 10s → 20s → 30s → 30s (then cap at 30s)
|
|
9
|
+
export const BACKOFF_SCHEDULE_MS = [2000, 5000, 10000, 20000, 30000, 30000];
|
|
10
|
+
/** Maximum total time from first failure before giving up, in ms. */
|
|
11
|
+
export const MAX_RECOVERY_DURATION_MS = 120_000;
|
|
12
|
+
/** Jitter factor (±20%). */
|
|
13
|
+
const JITTER_FACTOR = 0.2;
|
|
14
|
+
/**
|
|
15
|
+
* Apply ±20% jitter to a backoff delay. Deterministic seed not required —
|
|
16
|
+
* slight randomness prevents thundering herds across many assets.
|
|
17
|
+
*/
|
|
18
|
+
export function applyJitter(delayMs) {
|
|
19
|
+
const delta = delayMs * JITTER_FACTOR;
|
|
20
|
+
return Math.round(delayMs - delta + Math.random() * delta * 2);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Return the backoff delay for a given attempt index (0-based).
|
|
24
|
+
* Clamps to the last schedule entry beyond the end.
|
|
25
|
+
*/
|
|
26
|
+
export function backoffForAttempt(attempt) {
|
|
27
|
+
const raw = BACKOFF_SCHEDULE_MS[Math.min(attempt, BACKOFF_SCHEDULE_MS.length - 1)];
|
|
28
|
+
return applyJitter(raw);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* True when the total elapsed time since first failure would still be within
|
|
32
|
+
* the MAX_RECOVERY_DURATION_MS budget after the next retry.
|
|
33
|
+
*/
|
|
34
|
+
export function withinBudget(firstFailureAt, nextRetryAt) {
|
|
35
|
+
return nextRetryAt - firstFailureAt <= MAX_RECOVERY_DURATION_MS;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=recovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery.js","sourceRoot":"","sources":["../src/recovery.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAyEH,4EAA4E;AAC5E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAU,CAAC;AAErF,qEAAqE;AACrE,MAAM,CAAC,MAAM,wBAAwB,GAAG,OAAO,CAAC;AAEhD,4BAA4B;AAC5B,MAAM,aAAa,GAAG,GAAG,CAAC;AAE1B;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,MAAM,KAAK,GAAG,OAAO,GAAG,aAAa,CAAC;IACtC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC;AACjE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe;IAC/C,MAAM,GAAG,GAAG,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,mBAAmB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;IACnF,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;AAC1B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,cAAsB,EAAE,WAAmB;IACtE,OAAO,WAAW,GAAG,cAAc,IAAI,wBAAwB,CAAC;AAClE,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type * as React from "react";
|
|
2
|
+
import type { MediaDiagnosticAdapter, AssetStatusHint } from "./recovery.js";
|
|
3
|
+
export type { MediaDiagnosticAdapter, AssetStatusHint, DiagnosisResult, RecoveryState } from "./recovery.js";
|
|
2
4
|
export type MediaViewerType = "image" | "video" | "audio" | "other";
|
|
3
5
|
export type FallbackMode = "none" | "link";
|
|
4
6
|
export type BaseMediaProps = {
|
|
@@ -62,6 +64,18 @@ export type MediaPreviewProps = {
|
|
|
62
64
|
filename?: string;
|
|
63
65
|
mime?: string;
|
|
64
66
|
name?: string;
|
|
67
|
+
/**
|
|
68
|
+
* Injected diagnostic adapter. When provided, element load errors for
|
|
69
|
+
* assets the app believes are live will trigger the bounded recovery state
|
|
70
|
+
* machine. When omitted, the existing error fallback behavior is unchanged.
|
|
71
|
+
*/
|
|
72
|
+
recoveryAdapter?: MediaDiagnosticAdapter;
|
|
73
|
+
/**
|
|
74
|
+
* Optional app hint about the server-side lifecycle state of the asset.
|
|
75
|
+
* Drives which recovery phase is shown (e.g. processing → skeleton,
|
|
76
|
+
* finalizing → spinner, failed/rejected → hard error immediately).
|
|
77
|
+
*/
|
|
78
|
+
assetStatusHint?: AssetStatusHint;
|
|
65
79
|
};
|
|
66
80
|
/** Back-compat alias */
|
|
67
81
|
export type MediaViewerProps = MediaPreviewProps;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAE7E,YAAY,EAAE,sBAAsB,EAAE,eAAe,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE7G,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AACpE,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,CAAC;AAE3C,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,cAAc,GAAG;IAC9C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,cAAc,GAAG;IAC9C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;IACvC,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,cAAc,GAAG;IAC9C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;IACvC,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,eAAe,GAAG,MAAM,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;IACvC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IAKd;;;;OAIG;IACH,eAAe,CAAC,EAAE,sBAAsB,CAAC;IAEzC;;;;OAIG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC,CAAC;AAEF,wBAAwB;AACxB,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ttt-productions/media-viewer",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/ttt-productions/ttt-packages.git",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"prepublishOnly": "npm run clean && npm run build"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@ttt-productions/media-schemas": "^0.11.
|
|
36
|
+
"@ttt-productions/media-schemas": "^0.11.1",
|
|
37
37
|
"@ttt-productions/ui-core": "^0.11.0"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|