diff --git a/src/__tests__/lib/thirdParty.test.ts b/src/__tests__/lib/thirdParty.test.ts index 1c2e054f..c432fe75 100644 --- a/src/__tests__/lib/thirdParty.test.ts +++ b/src/__tests__/lib/thirdParty.test.ts @@ -134,6 +134,36 @@ describe("thirdParty script registry", () => { } }); + it("handles script load errors via onError callback", () => { + const onError = jest.fn(); + const mod = loadWithEnv({ + NEXT_PUBLIC_ANALYTICS_PROVIDER: "plausible", + NEXT_PUBLIC_ANALYTICS_DOMAIN: "proofofheart.xyz", + }); + + // Script configs returned by the registry have no onError by default. + for (const s of mod.getThirdPartyScripts()) { + mod.handleScriptError(s); + } + expect(onError).not.toHaveBeenCalled(); + + // When a consumer wires onError, handleScriptError invokes it. + const script = mod.getThirdPartyScripts()[0]; + mod.handleScriptError({ ...script, onError }); + expect(onError).toHaveBeenCalledTimes(1); + }); + + it("does not return duplicate script ids", () => { + const mod = loadWithEnv({ + NEXT_PUBLIC_ANALYTICS_PROVIDER: "plausible", + NEXT_PUBLIC_ANALYTICS_DOMAIN: "proofofheart.xyz", + NEXT_PUBLIC_SUPPORT_WIDGET_SRC: "https://widget.example.com/w.js", + }); + + const ids = mod.getThirdPartyScripts().map((s) => s.id); + expect(new Set(ids).size).toBe(ids.length); + }); + it("derives CSP origins from the configured scripts", () => { const mod = loadWithEnv({ NEXT_PUBLIC_ANALYTICS_PROVIDER: "plausible", diff --git a/src/components/ThirdPartyScripts.tsx b/src/components/ThirdPartyScripts.tsx index ec5947b0..2b212fd3 100644 --- a/src/components/ThirdPartyScripts.tsx +++ b/src/components/ThirdPartyScripts.tsx @@ -38,34 +38,19 @@ export default function ThirdPartyScripts() { return ( <> - {scripts.map(({ id, src, strategy, attributes }) => { - // #647 — Guard: `beforeInteractive` runs during SSR and blocks the main - // thread before hydration — exactly the problem this component exists to - // solve. Any misconfigured entry is demoted to `lazyOnload` at runtime - // and flagged in the dev console so it can be fixed in thirdParty.ts. - const safeStrategy = - strategy === "beforeInteractive" - ? (process.env.NODE_ENV !== "production" && - console.warn( - `[ThirdPartyScripts] Script "${id}" uses "beforeInteractive" which blocks` + - ` the main thread. Downgraded to "lazyOnload". Fix the strategy in thirdParty.ts.` - ), - "lazyOnload" as const) - : strategy; - - return ( -