mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-12 07:09:59 +00:00
fix(core): retry bpm-detective import after transient failure (#2736)
loadBpmDetective cached the promise returned by dynamic import even when that import rejected. A transient failure (network hiccup, bundler issue, missing module at first access) was therefore cached as null for the rest of the session, silently disabling BPM detection. - Reset the cached promise on import failure so the next call retries. - Only cache the default production import; custom loaders bypass the cache. - Make loadBpmDetective testable by accepting an optional importFn. - Add regression tests for failure/retry and module/default resolution.
This commit is contained in:
@@ -0,0 +1,41 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import { loadBpmDetective } from "./beatDetection";
|
||||||
|
|
||||||
|
describe("loadBpmDetective", () => {
|
||||||
|
it("resolves to null when the import is unavailable", async () => {
|
||||||
|
const detect = await loadBpmDetective(() => Promise.reject(new Error("missing")));
|
||||||
|
expect(detect).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("retries a previously failed import instead of caching null forever", async () => {
|
||||||
|
let shouldFail = true;
|
||||||
|
const importFn = async () => {
|
||||||
|
if (shouldFail) {
|
||||||
|
throw new Error("transient");
|
||||||
|
}
|
||||||
|
return { default: () => 120 };
|
||||||
|
};
|
||||||
|
|
||||||
|
const first = await loadBpmDetective(importFn);
|
||||||
|
expect(first).toBeNull();
|
||||||
|
|
||||||
|
shouldFail = false;
|
||||||
|
const second = await loadBpmDetective(importFn);
|
||||||
|
expect(second).not.toBeNull();
|
||||||
|
expect(second?.({} as AudioBuffer)).toBe(120);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses the default export if present, otherwise the module object", async () => {
|
||||||
|
const importFn = async () => ({ default: () => 90 });
|
||||||
|
const detect = await loadBpmDetective(importFn);
|
||||||
|
expect(detect?.({} as AudioBuffer)).toBe(90);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("falls back to the module object when there is no default export", async () => {
|
||||||
|
const fn = (buffer: AudioBuffer) => buffer.duration;
|
||||||
|
const importFn = async () => fn as unknown;
|
||||||
|
const detect = await loadBpmDetective(importFn);
|
||||||
|
expect(detect).toBe(fn);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -4,16 +4,32 @@
|
|||||||
// in the browser.
|
// in the browser.
|
||||||
type BpmDetect = (buffer: AudioBuffer) => number;
|
type BpmDetect = (buffer: AudioBuffer) => number;
|
||||||
let bpmDetectivePromise: Promise<BpmDetect | null> | null = null;
|
let bpmDetectivePromise: Promise<BpmDetect | null> | null = null;
|
||||||
function loadBpmDetective(): Promise<BpmDetect | null> {
|
|
||||||
if (!bpmDetectivePromise) {
|
const defaultBpmDetectiveImport = () =>
|
||||||
bpmDetectivePromise = import(
|
// @ts-ignore -- no type declarations for bpm-detective
|
||||||
// @ts-ignore -- no type declarations for bpm-detective
|
import("bpm-detective");
|
||||||
"bpm-detective"
|
|
||||||
)
|
export function loadBpmDetective(
|
||||||
.then((m) => ((m as { default?: BpmDetect }).default ?? (m as unknown as BpmDetect)) || null)
|
importFn: () => Promise<unknown> = defaultBpmDetectiveImport,
|
||||||
.catch(() => null);
|
): Promise<BpmDetect | null> {
|
||||||
|
const useCache = importFn === defaultBpmDetectiveImport;
|
||||||
|
if (useCache && bpmDetectivePromise) {
|
||||||
|
return bpmDetectivePromise;
|
||||||
}
|
}
|
||||||
return bpmDetectivePromise;
|
|
||||||
|
const promise = importFn()
|
||||||
|
.then((m) => ((m as { default?: BpmDetect }).default ?? (m as unknown as BpmDetect)) || null)
|
||||||
|
.catch(() => {
|
||||||
|
// Reset the cached promise so a transient import failure can be retried
|
||||||
|
// on the next call instead of being cached as null for the session.
|
||||||
|
if (useCache) bpmDetectivePromise = null;
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (useCache) {
|
||||||
|
bpmDetectivePromise = promise;
|
||||||
|
}
|
||||||
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
const WINDOW_SIZE = 1024;
|
const WINDOW_SIZE = 1024;
|
||||||
|
|||||||
Reference in New Issue
Block a user