import { scopeCssToComposition, wrapScopedCompositionScript } from "../compiler/compositionScoping"; import { markFlattenedInnerRoot } from "./flattenedRoot"; import { applyCssVariables, clearAppliedCssVariables, filterVariablesIfAbsent, parseHostVariableValues, readDeclaredDefaults, readRenderOverrides, } from "./getVariables"; type LoadExternalCompositionsParams = { injectedStyles: HTMLStyleElement[]; injectedScripts: HTMLScriptElement[]; injectedLinks: HTMLLinkElement[]; parseDimensionPx: (value: string | null) => string | null; onDiagnostic?: (payload: { code: string; details: Record; }) => void; }; type PendingScript = | { kind: "inline"; content: string; type: string; scopeCompositionId: string | null; } | { kind: "external"; src: string; type: string; }; const EXTERNAL_SCRIPT_LOAD_TIMEOUT_MS = 8000; const BARE_RELATIVE_PATH_RE = /^(?![a-zA-Z][a-zA-Z\d+\-.]*:)(?!\/\/)(?!\/)(?!\.\.?\/).+/; const CSS_URL_RE = /\burl\(\s*(["']?)([^)"']+)\1\s*\)/g; const PATH_ATTRS = ["src", "href"] as const; /** * Return true for URLs/prefixes that should never be rewritten — absolute * URLs, protocol-relative, data:, hash fragments, root-relative. Mirrors * the compiler's `isNonRelativeUrl` so server-side bundling and client-side * runtime rewrite use the same rules. */ function isNonRelativeRuntimeUrl(value: string): boolean { return ( !value || value.startsWith("http://") || value.startsWith("https://") || value.startsWith("//") || value.startsWith("data:") || value.startsWith("#") || value.startsWith("/") ); } /** * Resolve a relative asset path from a sub-composition's URL to one that * works in the live document. * * Server-side `inlineSubCompositions` rewrites `../foo.svg` from * `compositions/scene.html` to `foo.svg` (project root). When the runtime * mounts a sub-composition by fetching its HTML and importing its nodes * into the main document, no such rewriting happens — so a `