@hyperframes/parsers 0.7.59 → 0.7.61
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/assetResolution.d.ts +19 -0
- package/dist/assetResolution.js +87 -0
- package/dist/assetResolution.js.map +1 -0
- package/dist/composition.d.ts +3 -2
- package/dist/composition.js +299 -1
- package/dist/composition.js.map +1 -1
- package/dist/compositionContract.d.ts +77 -0
- package/dist/compositionContract.js +282 -0
- package/dist/compositionContract.js.map +1 -0
- package/dist/ffBinaries.d.ts +30 -0
- package/dist/ffBinaries.js +98 -0
- package/dist/ffBinaries.js.map +1 -0
- package/dist/gsapParser.d.ts +3 -3
- package/dist/gsapParserAcorn.d.ts +3 -3
- package/dist/gsapParserExports.d.ts +2 -2
- package/dist/{gsapSerialize-CTPaxKTV.d.ts → gsapSerialize-cLD37cjI.d.ts} +1 -1
- package/dist/gsapWriterAcorn.d.ts +2 -2
- package/dist/index.d.ts +21 -4
- package/dist/index.js +338 -28
- package/dist/index.js.map +1 -1
- package/dist/{types-CaeOJXdW.d.ts → types-ewozML_N.d.ts} +43 -1
- package/package.json +14 -2
package/dist/composition.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/fontAliases.ts","../src/utils/urlPath.ts","../src/compositionVariables.ts","../src/variableUsage.ts"],"sourcesContent":["// ── Composition data types ───────────────────────────────────────────────────\n// Moved from @hyperframes/core/core.types in the parsers extraction refactor.\n// These are the types produced and consumed by the parser pipeline.\n\nexport interface Asset {\n id: string;\n url: string;\n type: string;\n is_reference?: boolean;\n /** Duration in seconds for video/audio assets */\n duration?: number;\n}\n\n// ── Timeline types ──────────────────────────────────────────────────────────\n\nexport type TimelineElementType = \"video\" | \"image\" | \"text\" | \"audio\" | \"composition\";\nexport type MediaElementType = \"video\" | \"image\" | \"audio\";\n\nexport const CANVAS_DIMENSIONS = {\n landscape: { width: 1920, height: 1080 },\n portrait: { width: 1080, height: 1920 },\n \"landscape-4k\": { width: 3840, height: 2160 },\n \"portrait-4k\": { width: 2160, height: 3840 },\n square: { width: 1080, height: 1080 },\n \"square-4k\": { width: 2160, height: 2160 },\n} as const;\n\n// Single source of truth: derive the type from the table so adding a preset\n// extends the union automatically. Avoids the prior `as readonly CanvasResolution[]`\n// cast on `VALID_CANVAS_RESOLUTIONS` quietly drifting if the table grew but\n// the union didn't.\nexport type CanvasResolution = keyof typeof CANVAS_DIMENSIONS;\n\n// `Object.keys` ordering matches insertion order in `CANVAS_DIMENSIONS` on\n// every supported JS engine; tests pin the order in `index.test.ts`. Reorder\n// the table above with care.\nexport const VALID_CANVAS_RESOLUTIONS = Object.keys(\n CANVAS_DIMENSIONS,\n) as readonly CanvasResolution[];\n\nconst RESOLUTION_ALIASES: Record<string, CanvasResolution> = {\n \"1080p\": \"landscape\",\n hd: \"landscape\",\n \"1080p-portrait\": \"portrait\",\n \"portrait-1080p\": \"portrait\",\n \"4k\": \"landscape-4k\",\n uhd: \"landscape-4k\",\n \"4k-portrait\": \"portrait-4k\",\n \"1080p-square\": \"square\",\n \"square-1080p\": \"square\",\n \"4k-square\": \"square-4k\",\n};\n\n/**\n * Map a user-facing resolution string (canonical name or alias) to a\n * `CanvasResolution`. Returns undefined for unknown values so callers\n * can produce their own \"invalid\" UX (CLI exit, route validation, etc.).\n */\nexport function normalizeResolutionFlag(input: string | undefined): CanvasResolution | undefined {\n if (!input) return undefined;\n const lowered = input.toLowerCase();\n if ((VALID_CANVAS_RESOLUTIONS as readonly string[]).includes(lowered)) {\n return lowered as CanvasResolution;\n }\n return RESOLUTION_ALIASES[lowered];\n}\n\nexport interface TimelineElementBase {\n id: string;\n type: TimelineElementType;\n name: string;\n startTime: number;\n duration: number;\n zIndex: number;\n x?: number;\n y?: number;\n scale?: number;\n opacity?: number;\n}\n\nexport interface TimelineMediaElement extends TimelineElementBase {\n type: MediaElementType;\n src: string;\n mediaStartTime?: number;\n sourceDuration?: number;\n isAroll?: boolean;\n sourceWidth?: number;\n sourceHeight?: number;\n volume?: number; // 0-1 (0% to 100%), default 1.0\n hasAudio?: boolean; // For videos - indicates if video has audio track\n}\n\nexport interface WaveformData {\n peaks: number[];\n duration: number;\n sampleRate?: number;\n}\n\nexport interface TimelineTextElement extends TimelineElementBase {\n type: \"text\";\n content: string;\n color?: string;\n fontSize?: number;\n textShadow?: boolean;\n fontFamily?: string;\n fontWeight?: number;\n textOutline?: boolean;\n textOutlineColor?: string;\n textOutlineWidth?: number;\n textHighlight?: boolean;\n textHighlightColor?: string;\n textHighlightPadding?: number;\n textHighlightRadius?: number;\n}\n\nexport interface TimelineCompositionElement extends TimelineElementBase {\n type: \"composition\";\n src: string;\n compositionId: string;\n scale?: number;\n sourceDuration?: number;\n variableValues?: Record<string, string | number | boolean>;\n sourceWidth?: number;\n sourceHeight?: number;\n}\n\n// Composition Variable Types\nexport type CompositionVariableType =\n | \"string\"\n | \"number\"\n | \"color\"\n | \"boolean\"\n | \"enum\"\n | \"font\"\n | \"image\";\n\n/**\n * Runtime list of every valid `CompositionVariableType`. Use this anywhere\n * a Set/array of valid type strings is needed (lint rules, validators).\n * The `satisfies` guard turns adding a new variant to the union without\n * also adding it here into a compile error.\n */\nexport const COMPOSITION_VARIABLE_TYPES = [\n \"string\",\n \"number\",\n \"color\",\n \"boolean\",\n \"enum\",\n \"font\",\n \"image\",\n] as const satisfies readonly CompositionVariableType[];\n\nexport interface CompositionVariableBase {\n id: string;\n type: CompositionVariableType;\n label: string;\n description?: string;\n}\n\nexport interface StringVariable extends CompositionVariableBase {\n type: \"string\";\n default: string;\n placeholder?: string;\n maxLength?: number;\n}\n\nexport interface NumberVariable extends CompositionVariableBase {\n type: \"number\";\n default: number;\n min?: number;\n max?: number;\n step?: number;\n unit?: string;\n}\n\nexport interface ColorVariable extends CompositionVariableBase {\n type: \"color\";\n default: string;\n /** Brand role identifier, e.g. \"color:primary\". */\n brandRole?: string;\n}\n\nexport interface BooleanVariable extends CompositionVariableBase {\n type: \"boolean\";\n default: boolean;\n}\n\nexport interface EnumVariable extends CompositionVariableBase {\n type: \"enum\";\n default: string;\n options: { value: string; label: string }[];\n}\n\n/**\n * Font variable — value is a `{name, source}` object (object-valued; LOCKED §7).\n * `default` is the fallback font-family name string.\n * `source` is the font stylesheet URL (e.g. Google Fonts CSS).\n * `default_name` / `default_source` are the CSS-level fallbacks when the\n * brand font is absent.\n */\nexport interface FontVariable extends CompositionVariableBase {\n type: \"font\";\n /** Fallback font-family name, e.g. \"Inter\". */\n default: string;\n /** Font stylesheet URL (e.g. Google Fonts CSS link). */\n source?: string;\n /** CSS font-family name to use when source is unavailable, e.g. \"sans-serif\". */\n default_name?: string;\n /** Fallback font stylesheet URL (empty string = system font). */\n default_source?: string;\n}\n\n/**\n * Image variable — value is a `{url, …}` object (object-valued; LOCKED §7).\n * `default` is the fallback image URL string.\n * `brandRole` is an optional semantic label, e.g. \"logo:primary\".\n */\nexport interface ImageVariable extends CompositionVariableBase {\n type: \"image\";\n /** Fallback image URL. */\n default: string;\n /** Brand role identifier, e.g. \"logo:primary\". */\n brandRole?: string;\n}\n\nexport type CompositionVariable =\n | StringVariable\n | NumberVariable\n | ColorVariable\n | BooleanVariable\n | EnumVariable\n | FontVariable\n | ImageVariable;\n\nexport interface CompositionSpec {\n id: string;\n duration: number;\n variables: CompositionVariable[];\n}\n\nexport type TimelineElement =\n | TimelineMediaElement\n | TimelineTextElement\n | TimelineCompositionElement;\n\nexport function isTextElement(el: TimelineElement): el is TimelineTextElement {\n return el.type === \"text\";\n}\n\nexport function isMediaElement(el: TimelineElement): el is TimelineMediaElement {\n return el.type === \"video\" || el.type === \"image\" || el.type === \"audio\";\n}\n\nexport function isCompositionElement(el: TimelineElement): el is TimelineCompositionElement {\n return el.type === \"composition\";\n}\n\nexport interface MediaFile {\n id: string;\n name: string;\n type: TimelineElementType;\n src: string;\n file?: File;\n duration?: number;\n compositionId?: string;\n sourceWidth?: number; // Intrinsic width for compositions\n sourceHeight?: number; // Intrinsic height for compositions\n}\n\nexport const TIMELINE_COLORS: Record<TimelineElementType, string> = {\n video: \"#ec4899\",\n image: \"#3b82f6\",\n text: \"#06b6d4\",\n audio: \"#10b981\",\n composition: \"#f97316\",\n};\n\nexport const DEFAULT_DURATIONS: Record<TimelineElementType, number> = {\n video: 5,\n image: 5,\n text: 2,\n audio: 5,\n composition: 5,\n};\n\nexport interface CompositionAPI {\n id: string;\n duration: number;\n seek(time: number): void;\n getTime(): number;\n getDuration(): number;\n}\n\n// ── Player API types (used by runtime) ────────────────────────────────────\n\nexport interface PlayerAPI {\n play(): void;\n pause(): void;\n seek(time: number, options?: { keepPlaying?: boolean }): void;\n getTime(): number;\n getDuration(): number;\n isPlaying(): boolean;\n getMainTimeline(): unknown;\n getElementBounds(elementId: string): void;\n getElementsAtPoint(x: number, y: number): void;\n setElementPosition(elementId: string, x: number, y: number): void;\n previewElementPosition(elementId: string, x: number, y: number): void;\n setElementKeyframes(\n elementId: string,\n keyframes: Array<{\n id: string;\n time: number;\n properties: { x?: number; y?: number };\n }> | null,\n ): void;\n setElementScale(elementId: string, scale: number): void;\n setElementFontSize(elementId: string, fontSize: number): void;\n setElementTextContent(elementId: string, content: string): void;\n setElementTextColor(elementId: string, color: string): void;\n setElementTextShadow(elementId: string, enabled: boolean): void;\n setElementTextFontWeight(elementId: string, weight: number): void;\n setElementTextFontFamily(elementId: string, fontFamily: string): void;\n setElementTextOutline(elementId: string, enabled: boolean, color?: string, width?: number): void;\n setElementTextHighlight(\n elementId: string,\n enabled: boolean,\n color?: string,\n padding?: number,\n radius?: number,\n ): void;\n setElementVolume(elementId: string, volume: number): void;\n setStageZoom(scale: number, focusX: number, focusY: number): void;\n getStageZoom(): { scale: number; focusX: number; focusY: number };\n setStageZoomKeyframes(\n keyframes: Array<{\n id: string;\n time: number;\n zoom: { scale: number; focusX: number; focusY: number };\n ease?: string;\n }> | null,\n ): void;\n getStageZoomKeyframes(): Array<{\n id: string;\n time: number;\n zoom: { scale: number; focusX: number; focusY: number };\n ease?: string;\n }>;\n addElement(data: AddElementData): boolean;\n removeElement(elementId: string): boolean;\n updateElementTiming(elementId: string, start?: number, end?: number): boolean;\n setElementTiming(\n elementId: string,\n startTime: number,\n duration: number,\n mediaStartTime?: number,\n ): void;\n updateElementSrc(elementId: string, src: string): boolean;\n updateElementLayer(elementId: string, zIndex: number): boolean;\n updateElementBasePosition(elementId: string, x?: number, y?: number, scale?: number): boolean;\n markTimelineDirty(): void;\n isTimelineDirty(): boolean;\n rebuildTimeline(): void;\n ensureTimeline(): void;\n enableRenderMode(): void;\n disableRenderMode(): void;\n renderSeek(time: number, options?: { suppressEvents?: boolean }): void;\n getElementVisibility(elementId: string): { visible: boolean; opacity?: number };\n getVisibleElements(): Array<{ id: string; tagName: string; start: number; end: number }>;\n getRenderState(): {\n time: number;\n duration: number;\n isPlaying: boolean;\n renderMode: boolean;\n timelineDirty: boolean;\n };\n}\n\nexport interface AddElementData {\n id: string;\n type: \"video\" | \"image\" | \"text\" | \"audio\" | \"composition\";\n name?: string;\n src?: string;\n content?: string;\n start: number;\n end: number;\n zIndex?: number;\n x?: number;\n y?: number;\n scale?: number;\n fontSize?: number;\n color?: string;\n textShadow?: boolean;\n fontWeight?: number;\n textOutline?: boolean;\n textOutlineColor?: string;\n textOutlineWidth?: number;\n textHighlight?: boolean;\n textHighlightColor?: string;\n textHighlightPadding?: number;\n textHighlightRadius?: number;\n compositionId?: string;\n sourceWidth?: number;\n sourceHeight?: number;\n isAroll?: boolean;\n}\n\nexport interface ValidationResult {\n valid: boolean;\n errors: string[];\n warnings: string[];\n}\n\nexport interface CompositionAsset {\n id: string;\n name: string;\n type: \"composition\";\n src: string;\n duration: number;\n compositionId: string;\n thumbnail?: string;\n}\n\nexport interface Keyframe {\n id: string;\n time: number;\n properties: Partial<KeyframeProperties>;\n ease?: string;\n}\n\nexport interface KeyframeProperties {\n x: number;\n y: number;\n opacity: number;\n scale: number;\n scaleX: number;\n scaleY: number;\n rotation: number;\n width: number;\n height: number;\n}\n\nexport interface ElementKeyframes {\n elementId: string;\n keyframes: Keyframe[];\n}\n\nexport interface StageZoom {\n scale: number;\n focusX: number;\n focusY: number;\n}\n\nexport interface StageZoomKeyframe {\n id: string;\n time: number;\n zoom: StageZoom;\n ease?: string;\n}\n\nexport function getDefaultStageZoom(resolution: CanvasResolution): StageZoom {\n const { width, height } = CANVAS_DIMENSIONS[resolution];\n return {\n scale: 1,\n focusX: width / 2,\n focusY: height / 2,\n };\n}\n","/**\n * Single source of truth for the deterministic font alias map. Both the\n * producer's @font-face injector and the core lint rules import from here,\n * eliminating manual drift between the two.\n *\n * Keys are lowercase font family names. Values are canonical font slugs\n * matching CANONICAL_FONTS keys in the producer's deterministicFonts module.\n */\nexport const FONT_ALIAS_MAP = {\n // ── Canonical bundled fonts (self-referencing) ────────────────────────\n inter: \"inter\",\n montserrat: \"montserrat\",\n outfit: \"outfit\",\n nunito: \"nunito\",\n oswald: \"oswald\",\n \"league gothic\": \"league-gothic\",\n \"archivo black\": \"archivo-black\",\n \"space mono\": \"space-mono\",\n \"ibm plex mono\": \"ibm-plex-mono\",\n \"jetbrains mono\": \"jetbrains-mono\",\n \"eb garamond\": \"eb-garamond\",\n \"playfair display\": \"playfair-display\",\n \"source code pro\": \"source-code-pro\",\n \"noto sans jp\": \"noto-sans-jp\",\n roboto: \"roboto\",\n \"open sans\": \"open-sans\",\n lato: \"lato\",\n poppins: \"poppins\",\n\n // ── Common aliases → nearest canonical ────────────────────────────────\n \"helvetica neue\": \"inter\",\n helvetica: \"inter\",\n arial: \"inter\",\n \"helvetica bold\": \"inter\",\n futura: \"montserrat\",\n \"din alternate\": \"montserrat\",\n \"arial black\": \"montserrat\",\n \"bebas neue\": \"league-gothic\",\n \"courier new\": \"jetbrains-mono\",\n courier: \"jetbrains-mono\",\n garamond: \"eb-garamond\",\n \"noto sans japanese\": \"noto-sans-jp\",\n \"segoe ui\": \"roboto\",\n\n // ── macOS sans-serif system fonts → inter ─────────────────────────────\n \"sf pro\": \"inter\",\n \"sf pro display\": \"inter\",\n \"sf pro text\": \"inter\",\n \"sf pro rounded\": \"inter\",\n avenir: \"inter\",\n \"avenir next\": \"inter\",\n \"lucida grande\": \"inter\",\n geneva: \"inter\",\n optima: \"inter\",\n\n // ── Windows sans-serif system fonts → inter ───────────────────────────\n verdana: \"inter\",\n tahoma: \"inter\",\n \"trebuchet ms\": \"inter\",\n calibri: \"inter\",\n candara: \"inter\",\n corbel: \"inter\",\n \"lucida sans\": \"inter\",\n \"lucida sans unicode\": \"inter\",\n\n // ── Linux sans-serif system fonts → inter ─────────────────────────────\n \"noto sans\": \"inter\",\n \"dejavu sans\": \"inter\",\n \"liberation sans\": \"inter\",\n\n // ── Monospace system fonts → jetbrains-mono ───────────────────────────\n \"sf mono\": \"jetbrains-mono\",\n menlo: \"jetbrains-mono\",\n monaco: \"jetbrains-mono\",\n consolas: \"jetbrains-mono\",\n \"lucida console\": \"jetbrains-mono\",\n \"lucida sans typewriter\": \"jetbrains-mono\",\n \"andale mono\": \"jetbrains-mono\",\n \"dejavu sans mono\": \"jetbrains-mono\",\n \"liberation mono\": \"jetbrains-mono\",\n\n // ── Serif system fonts → eb-garamond ──────────────────────────────────\n georgia: \"eb-garamond\",\n palatino: \"eb-garamond\",\n \"palatino linotype\": \"eb-garamond\",\n \"book antiqua\": \"eb-garamond\",\n cambria: \"eb-garamond\",\n times: \"eb-garamond\",\n \"times new roman\": \"eb-garamond\",\n \"dejavu serif\": \"eb-garamond\",\n \"liberation serif\": \"eb-garamond\",\n} satisfies Readonly<Record<string, string>>;\n\nexport const FONT_ALIAS_KEYS: ReadonlySet<string> = new Set(Object.keys(FONT_ALIAS_MAP));\n\n/**\n * Human-readable display names for canonical font slugs. Used by the lint\n * rule to tell authors what their aliased font will render as.\n */\nexport const CANONICAL_FONT_DISPLAY_NAMES: Readonly<Record<string, string>> = {\n inter: \"Inter\",\n montserrat: \"Montserrat\",\n outfit: \"Outfit\",\n nunito: \"Nunito\",\n oswald: \"Oswald\",\n \"league-gothic\": \"League Gothic\",\n \"archivo-black\": \"Archivo Black\",\n \"space-mono\": \"Space Mono\",\n \"ibm-plex-mono\": \"IBM Plex Mono\",\n \"jetbrains-mono\": \"JetBrains Mono\",\n \"eb-garamond\": \"EB Garamond\",\n \"playfair-display\": \"Playfair Display\",\n \"source-code-pro\": \"Source Code Pro\",\n \"noto-sans-jp\": \"Noto Sans JP\",\n roboto: \"Roboto\",\n \"open-sans\": \"Open Sans\",\n lato: \"Lato\",\n poppins: \"Poppins\",\n};\n\n/**\n * Resolve a font alias to its canonical display name, or undefined if the\n * alias is not in the map.\n */\nexport function resolveAliasDisplayName(alias: string): string | undefined {\n const slug = (FONT_ALIAS_MAP as Record<string, string>)[alias.toLowerCase()];\n if (!slug) return undefined;\n return CANONICAL_FONT_DISPLAY_NAMES[slug];\n}\n","export function decodeUrlPathVariants(path: string): string[] {\n const variants = [path];\n try {\n const decoded = decodeURIComponent(path);\n if (decoded !== path) variants.unshift(decoded);\n } catch {\n // Malformed percent sequences may be literal filesystem names.\n }\n\n return variants;\n}\n","/**\n * Browser-safe parser for the `data-composition-variables` schema attribute.\n * Lives outside htmlParser.ts so browser consumers (SDK, Studio, lint) can\n * import it via `@hyperframes/parsers/composition` without pulling the\n * linkedom/Node HTML-parser machinery from the main entry.\n */\n\nimport type { CompositionVariable, CompositionVariableType } from \"./types.js\";\n\n/**\n * Required typeof for each variable type's `default`. For font the default is\n * the font-family name string; for image it is the fallback URL string —\n * extra metadata fields on both are optional and not validated here.\n */\nconst DEFAULT_TYPEOF: Record<CompositionVariableType, \"string\" | \"number\" | \"boolean\"> = {\n string: \"string\",\n number: \"number\",\n color: \"string\",\n boolean: \"boolean\",\n enum: \"string\",\n font: \"string\",\n image: \"string\",\n};\n\n/**\n * Scalar variable values (string/number/boolean) are the ones that flow into\n * CSS custom props and text bindings; font/image values are object-shaped.\n * Shared so the SDK's CSS-compat writes, the runtime bindings, and Studio's\n * display logic can never disagree on what \"scalar\" means.\n */\nexport function isScalarVariableValue(value: unknown): value is string | number | boolean {\n return typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\";\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null;\n}\n\nfunction isVariableType(t: unknown): t is CompositionVariableType {\n return typeof t === \"string\" && t in DEFAULT_TYPEOF;\n}\n\n/**\n * True when the value is a structurally valid variable declaration: id, label,\n * a known type, a default matching that type, and options[] for enums. The\n * same predicate parseCompositionVariables filters with — exported so writers\n * (SDK declaration ops, Studio forms) can validate before persisting.\n */\nexport function isCompositionVariable(v: unknown): v is CompositionVariable {\n if (!isRecord(v)) return false;\n if (typeof v.id !== \"string\" || typeof v.label !== \"string\") return false;\n if (!isVariableType(v.type)) return false;\n if (typeof v.default !== DEFAULT_TYPEOF[v.type]) return false;\n if (v.type === \"enum\" && !Array.isArray(v.options)) return false;\n return true;\n}\n\n/**\n * Parse the typed variable declarations from an element's\n * `data-composition-variables` attribute. Malformed entries (wrong shape,\n * unknown type, default not matching the declared type) are dropped; an\n * absent attribute, invalid JSON, or a non-array payload yields `[]`.\n */\nexport function parseCompositionVariables(htmlEl: Element): CompositionVariable[] {\n const variablesAttr = htmlEl.getAttribute(\"data-composition-variables\");\n if (!variablesAttr) {\n return [];\n }\n\n try {\n const parsed = JSON.parse(variablesAttr);\n if (!Array.isArray(parsed)) {\n return [];\n }\n return parsed.filter(isCompositionVariable);\n } catch {\n return [];\n }\n}\n","/**\n * Browser-safe static scan for composition-variable reads in script text.\n *\n * Compositions read variables by calling the runtime API — `getVariables()`\n * bare (sub-comp scoped shadow) or via `__hyperframes.getVariables()` /\n * `window.__hyperframes.getVariables()` — and there is no DOM-attribute\n * binding to scan, so \"which variables does this composition use\" can only be\n * derived from the scripts. This is a best-effort static analysis: the\n * patterns agents actually write (destructuring, member access, a single\n * alias variable) resolve to ids; anything opaque flips `scanIncomplete`\n * so consumers can present usage as a lower bound instead of a fact.\n *\n * AST nodes are handled untyped (same convention as gsapParserAcorn.ts) —\n * acorn's structural types don't survive acorn-walk's visitor signatures.\n */\n\nimport * as acorn from \"acorn\";\nimport * as acornWalk from \"acorn-walk\";\n\nexport interface VariableUsageScan {\n /** Variable ids statically read by the script, in first-seen order. */\n usedIds: string[];\n /**\n * True when the script accesses variables in a way the scan cannot resolve\n * (computed keys, rest spreads, the values object escaping into a call…) or\n * when the script fails to parse — usedIds is then a lower bound.\n */\n scanIncomplete: boolean;\n}\n\ninterface Sink {\n use(id: string): void;\n incomplete(): void;\n}\n\n// oxlint-disable no-explicit-any -- untyped acorn AST traversal, see header\n\nfunction isGetVariablesCallee(callee: any): boolean {\n if (callee?.type === \"Identifier\") return callee.name === \"getVariables\";\n if (callee?.type === \"MemberExpression\" && !callee.computed) {\n return callee.property?.type === \"Identifier\" && callee.property.name === \"getVariables\";\n }\n return false;\n}\n\n/** Collect ids from an ObjectPattern destructuring of the values object. */\n// Exhaustive AST-node classification — branchy by nature, same as gsapParserAcorn.\n// fallow-ignore-next-line complexity\nfunction collectFromObjectPattern(pattern: any, out: Sink): void {\n for (const prop of pattern.properties ?? []) {\n if (prop?.type === \"RestElement\") {\n out.incomplete();\n continue;\n }\n if (prop?.type !== \"Property\") continue;\n if (prop.computed === true) {\n out.incomplete();\n } else if (prop.key?.type === \"Identifier\") {\n out.use(String(prop.key.name));\n } else if (prop.key?.type === \"Literal\" && typeof prop.key.value === \"string\") {\n out.use(prop.key.value);\n } else {\n out.incomplete();\n }\n }\n}\n\n/** Collect an id from a MemberExpression reading the values object. */\nfunction collectFromMemberAccess(member: any, out: Sink): void {\n if (member.computed !== true && member.property?.type === \"Identifier\") {\n out.use(String(member.property.name));\n } else if (\n member.computed === true &&\n member.property?.type === \"Literal\" &&\n typeof member.property.value === \"string\"\n ) {\n out.use(member.property.value);\n } else {\n out.incomplete();\n }\n}\n\n/**\n * Classify one read of the values object (a getVariables() call result or an\n * alias holding it) by its immediate syntactic context. Returns the alias\n * name when the value is bound to a plain variable (`const vars = …`).\n */\n// fallow-ignore-next-line complexity\nfunction classifyValueRead(parent: any, valueNode: any, out: Sink): string | null {\n if (!parent || parent.type === \"ExpressionStatement\") {\n // Bare statement — value unused, nothing read.\n return null;\n }\n if (parent.type === \"MemberExpression\" && parent.object === valueNode) {\n collectFromMemberAccess(parent, out);\n return null;\n }\n if (parent.type === \"VariableDeclarator\" && parent.init === valueNode) {\n if (parent.id?.type === \"ObjectPattern\") {\n collectFromObjectPattern(parent.id, out);\n return null;\n }\n if (parent.id?.type === \"Identifier\") return String(parent.id.name);\n out.incomplete();\n return null;\n }\n // The values object escapes (argument, return, spread, assignment…) —\n // reads beyond this point are invisible to the scan.\n out.incomplete();\n return null;\n}\n\nexport function scanVariableUsage(scriptText: string): VariableUsageScan {\n const usedIds: string[] = [];\n const seen = new Set<string>();\n let scanIncomplete = false;\n\n const sink: Sink = {\n use(id: string) {\n if (!seen.has(id)) {\n seen.add(id);\n usedIds.push(id);\n }\n },\n incomplete() {\n scanIncomplete = true;\n },\n };\n\n let ast: any;\n try {\n ast = acorn.parse(scriptText, { ecmaVersion: \"latest\", sourceType: \"script\" });\n } catch {\n return { usedIds: [], scanIncomplete: true };\n }\n\n const aliases = new Set<string>();\n\n // Pass 1: classify every getVariables() call by its parent context.\n acornWalk.ancestor(ast, {\n CallExpression(node: any, _: unknown, ancestors: any[]) {\n if (!isGetVariablesCallee(node.callee)) return;\n const parent = ancestors[ancestors.length - 2];\n const alias = classifyValueRead(parent, node, sink);\n if (alias) aliases.add(alias);\n },\n } as any);\n\n // Pass 2: classify every reference to an alias of the values object.\n // Scope-naive by design: an unrelated same-named identifier can only make\n // the scan report extra ids or flip scanIncomplete, never miss a read.\n if (aliases.size > 0) {\n acornWalk.ancestor(ast, {\n // fallow-ignore-next-line complexity\n Identifier(node: any, _: unknown, ancestors: any[]) {\n if (!aliases.has(String(node.name))) return;\n const parent = ancestors[ancestors.length - 2];\n if (!parent) return;\n // Skip the declarator that introduced the alias and property-position\n // identifiers that merely share the name.\n if (parent.type === \"VariableDeclarator\" && parent.id === node) return;\n if (parent.type === \"MemberExpression\" && parent.property === node) return;\n if (parent.type === \"Property\" && parent.key === node && parent.computed !== true) return;\n // Chained aliases (const v2 = vars) are not followed — flag instead\n // of silently missing reads through the second name.\n if (classifyValueRead(parent, node, sink)) sink.incomplete();\n },\n } as any);\n }\n\n return { usedIds, scanIncomplete };\n}\n"],"mappings":";AAkBO,IAAM,oBAAoB;AAAA,EAC/B,WAAW,EAAE,OAAO,MAAM,QAAQ,KAAK;AAAA,EACvC,UAAU,EAAE,OAAO,MAAM,QAAQ,KAAK;AAAA,EACtC,gBAAgB,EAAE,OAAO,MAAM,QAAQ,KAAK;AAAA,EAC5C,eAAe,EAAE,OAAO,MAAM,QAAQ,KAAK;AAAA,EAC3C,QAAQ,EAAE,OAAO,MAAM,QAAQ,KAAK;AAAA,EACpC,aAAa,EAAE,OAAO,MAAM,QAAQ,KAAK;AAC3C;AAWO,IAAM,2BAA2B,OAAO;AAAA,EAC7C;AACF;AAEA,IAAM,qBAAuD;AAAA,EAC3D,SAAS;AAAA,EACT,IAAI;AAAA,EACJ,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,MAAM;AAAA,EACN,KAAK;AAAA,EACL,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,aAAa;AACf;AAOO,SAAS,wBAAwB,OAAyD;AAC/F,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,UAAU,MAAM,YAAY;AAClC,MAAK,yBAA+C,SAAS,OAAO,GAAG;AACrE,WAAO;AAAA,EACT;AACA,SAAO,mBAAmB,OAAO;AACnC;AA6EO,IAAM,6BAA6B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA+FO,SAAS,cAAc,IAAgD;AAC5E,SAAO,GAAG,SAAS;AACrB;AAEO,SAAS,eAAe,IAAiD;AAC9E,SAAO,GAAG,SAAS,WAAW,GAAG,SAAS,WAAW,GAAG,SAAS;AACnE;AAEO,SAAS,qBAAqB,IAAuD;AAC1F,SAAO,GAAG,SAAS;AACrB;AAcO,IAAM,kBAAuD;AAAA,EAClE,OAAO;AAAA,EACP,OAAO;AAAA,EACP,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aAAa;AACf;AAEO,IAAM,oBAAyD;AAAA,EACpE,OAAO;AAAA,EACP,OAAO;AAAA,EACP,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aAAa;AACf;AAgLO,SAAS,oBAAoB,YAAyC;AAC3E,QAAM,EAAE,OAAO,OAAO,IAAI,kBAAkB,UAAU;AACtD,SAAO;AAAA,IACL,OAAO;AAAA,IACP,QAAQ,QAAQ;AAAA,IAChB,QAAQ,SAAS;AAAA,EACnB;AACF;;;AC1cO,IAAM,iBAAiB;AAAA;AAAA,EAE5B,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,gBAAgB;AAAA,EAChB,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,MAAM;AAAA,EACN,SAAS;AAAA;AAAA,EAGT,kBAAkB;AAAA,EAClB,WAAW;AAAA,EACX,OAAO;AAAA,EACP,kBAAkB;AAAA,EAClB,QAAQ;AAAA,EACR,iBAAiB;AAAA,EACjB,eAAe;AAAA,EACf,cAAc;AAAA,EACd,eAAe;AAAA,EACf,SAAS;AAAA,EACT,UAAU;AAAA,EACV,sBAAsB;AAAA,EACtB,YAAY;AAAA;AAAA,EAGZ,UAAU;AAAA,EACV,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,QAAQ;AAAA,EACR,QAAQ;AAAA;AAAA,EAGR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,uBAAuB;AAAA;AAAA,EAGvB,aAAa;AAAA,EACb,eAAe;AAAA,EACf,mBAAmB;AAAA;AAAA,EAGnB,WAAW;AAAA,EACX,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,kBAAkB;AAAA,EAClB,0BAA0B;AAAA,EAC1B,eAAe;AAAA,EACf,oBAAoB;AAAA,EACpB,mBAAmB;AAAA;AAAA,EAGnB,SAAS;AAAA,EACT,UAAU;AAAA,EACV,qBAAqB;AAAA,EACrB,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,OAAO;AAAA,EACP,mBAAmB;AAAA,EACnB,gBAAgB;AAAA,EAChB,oBAAoB;AACtB;AAEO,IAAM,kBAAuC,IAAI,IAAI,OAAO,KAAK,cAAc,CAAC;AAMhF,IAAM,+BAAiE;AAAA,EAC5E,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,gBAAgB;AAAA,EAChB,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,MAAM;AAAA,EACN,SAAS;AACX;AAMO,SAAS,wBAAwB,OAAmC;AACzE,QAAM,OAAQ,eAA0C,MAAM,YAAY,CAAC;AAC3E,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,6BAA6B,IAAI;AAC1C;;;AChIO,SAAS,sBAAsB,MAAwB;AAC5D,QAAM,WAAW,CAAC,IAAI;AACtB,MAAI;AACF,UAAM,UAAU,mBAAmB,IAAI;AACvC,QAAI,YAAY,KAAM,UAAS,QAAQ,OAAO;AAAA,EAChD,QAAQ;AAAA,EAER;AAEA,SAAO;AACT;;;ACIA,IAAM,iBAAmF;AAAA,EACvF,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,SAAS;AAAA,EACT,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AAQO,SAAS,sBAAsB,OAAoD;AACxF,SAAO,OAAO,UAAU,YAAY,OAAO,UAAU,YAAY,OAAO,UAAU;AACpF;AAEA,SAAS,SAAS,GAA0C;AAC1D,SAAO,OAAO,MAAM,YAAY,MAAM;AACxC;AAEA,SAAS,eAAe,GAA0C;AAChE,SAAO,OAAO,MAAM,YAAY,KAAK;AACvC;AAQO,SAAS,sBAAsB,GAAsC;AAC1E,MAAI,CAAC,SAAS,CAAC,EAAG,QAAO;AACzB,MAAI,OAAO,EAAE,OAAO,YAAY,OAAO,EAAE,UAAU,SAAU,QAAO;AACpE,MAAI,CAAC,eAAe,EAAE,IAAI,EAAG,QAAO;AACpC,MAAI,OAAO,EAAE,YAAY,eAAe,EAAE,IAAI,EAAG,QAAO;AACxD,MAAI,EAAE,SAAS,UAAU,CAAC,MAAM,QAAQ,EAAE,OAAO,EAAG,QAAO;AAC3D,SAAO;AACT;AAQO,SAAS,0BAA0B,QAAwC;AAChF,QAAM,gBAAgB,OAAO,aAAa,4BAA4B;AACtE,MAAI,CAAC,eAAe;AAClB,WAAO,CAAC;AAAA,EACV;AAEA,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,aAAa;AACvC,QAAI,CAAC,MAAM,QAAQ,MAAM,GAAG;AAC1B,aAAO,CAAC;AAAA,IACV;AACA,WAAO,OAAO,OAAO,qBAAqB;AAAA,EAC5C,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;;;AC9DA,YAAY,WAAW;AACvB,YAAY,eAAe;AAoB3B,SAAS,qBAAqB,QAAsB;AAClD,MAAI,QAAQ,SAAS,aAAc,QAAO,OAAO,SAAS;AAC1D,MAAI,QAAQ,SAAS,sBAAsB,CAAC,OAAO,UAAU;AAC3D,WAAO,OAAO,UAAU,SAAS,gBAAgB,OAAO,SAAS,SAAS;AAAA,EAC5E;AACA,SAAO;AACT;AAKA,SAAS,yBAAyB,SAAc,KAAiB;AAC/D,aAAW,QAAQ,QAAQ,cAAc,CAAC,GAAG;AAC3C,QAAI,MAAM,SAAS,eAAe;AAChC,UAAI,WAAW;AACf;AAAA,IACF;AACA,QAAI,MAAM,SAAS,WAAY;AAC/B,QAAI,KAAK,aAAa,MAAM;AAC1B,UAAI,WAAW;AAAA,IACjB,WAAW,KAAK,KAAK,SAAS,cAAc;AAC1C,UAAI,IAAI,OAAO,KAAK,IAAI,IAAI,CAAC;AAAA,IAC/B,WAAW,KAAK,KAAK,SAAS,aAAa,OAAO,KAAK,IAAI,UAAU,UAAU;AAC7E,UAAI,IAAI,KAAK,IAAI,KAAK;AAAA,IACxB,OAAO;AACL,UAAI,WAAW;AAAA,IACjB;AAAA,EACF;AACF;AAGA,SAAS,wBAAwB,QAAa,KAAiB;AAC7D,MAAI,OAAO,aAAa,QAAQ,OAAO,UAAU,SAAS,cAAc;AACtE,QAAI,IAAI,OAAO,OAAO,SAAS,IAAI,CAAC;AAAA,EACtC,WACE,OAAO,aAAa,QACpB,OAAO,UAAU,SAAS,aAC1B,OAAO,OAAO,SAAS,UAAU,UACjC;AACA,QAAI,IAAI,OAAO,SAAS,KAAK;AAAA,EAC/B,OAAO;AACL,QAAI,WAAW;AAAA,EACjB;AACF;AAQA,SAAS,kBAAkB,QAAa,WAAgB,KAA0B;AAChF,MAAI,CAAC,UAAU,OAAO,SAAS,uBAAuB;AAEpD,WAAO;AAAA,EACT;AACA,MAAI,OAAO,SAAS,sBAAsB,OAAO,WAAW,WAAW;AACrE,4BAAwB,QAAQ,GAAG;AACnC,WAAO;AAAA,EACT;AACA,MAAI,OAAO,SAAS,wBAAwB,OAAO,SAAS,WAAW;AACrE,QAAI,OAAO,IAAI,SAAS,iBAAiB;AACvC,+BAAyB,OAAO,IAAI,GAAG;AACvC,aAAO;AAAA,IACT;AACA,QAAI,OAAO,IAAI,SAAS,aAAc,QAAO,OAAO,OAAO,GAAG,IAAI;AAClE,QAAI,WAAW;AACf,WAAO;AAAA,EACT;AAGA,MAAI,WAAW;AACf,SAAO;AACT;AAEO,SAAS,kBAAkB,YAAuC;AACvE,QAAM,UAAoB,CAAC;AAC3B,QAAM,OAAO,oBAAI,IAAY;AAC7B,MAAI,iBAAiB;AAErB,QAAM,OAAa;AAAA,IACjB,IAAI,IAAY;AACd,UAAI,CAAC,KAAK,IAAI,EAAE,GAAG;AACjB,aAAK,IAAI,EAAE;AACX,gBAAQ,KAAK,EAAE;AAAA,MACjB;AAAA,IACF;AAAA,IACA,aAAa;AACX,uBAAiB;AAAA,IACnB;AAAA,EACF;AAEA,MAAI;AACJ,MAAI;AACF,UAAY,YAAM,YAAY,EAAE,aAAa,UAAU,YAAY,SAAS,CAAC;AAAA,EAC/E,QAAQ;AACN,WAAO,EAAE,SAAS,CAAC,GAAG,gBAAgB,KAAK;AAAA,EAC7C;AAEA,QAAM,UAAU,oBAAI,IAAY;AAGhC,EAAU,mBAAS,KAAK;AAAA,IACtB,eAAe,MAAW,GAAY,WAAkB;AACtD,UAAI,CAAC,qBAAqB,KAAK,MAAM,EAAG;AACxC,YAAM,SAAS,UAAU,UAAU,SAAS,CAAC;AAC7C,YAAM,QAAQ,kBAAkB,QAAQ,MAAM,IAAI;AAClD,UAAI,MAAO,SAAQ,IAAI,KAAK;AAAA,IAC9B;AAAA,EACF,CAAQ;AAKR,MAAI,QAAQ,OAAO,GAAG;AACpB,IAAU,mBAAS,KAAK;AAAA;AAAA,MAEtB,WAAW,MAAW,GAAY,WAAkB;AAClD,YAAI,CAAC,QAAQ,IAAI,OAAO,KAAK,IAAI,CAAC,EAAG;AACrC,cAAM,SAAS,UAAU,UAAU,SAAS,CAAC;AAC7C,YAAI,CAAC,OAAQ;AAGb,YAAI,OAAO,SAAS,wBAAwB,OAAO,OAAO,KAAM;AAChE,YAAI,OAAO,SAAS,sBAAsB,OAAO,aAAa,KAAM;AACpE,YAAI,OAAO,SAAS,cAAc,OAAO,QAAQ,QAAQ,OAAO,aAAa,KAAM;AAGnF,YAAI,kBAAkB,QAAQ,MAAM,IAAI,EAAG,MAAK,WAAW;AAAA,MAC7D;AAAA,IACF,CAAQ;AAAA,EACV;AAEA,SAAO,EAAE,SAAS,eAAe;AACnC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/fontAliases.ts","../src/utils/urlPath.ts","../src/compositionVariables.ts","../src/variableUsage.ts","../src/compositionContract.ts"],"sourcesContent":["// ── Composition data types ───────────────────────────────────────────────────\n// Moved from @hyperframes/core/core.types in the parsers extraction refactor.\n// These are the types produced and consumed by the parser pipeline.\n\nexport interface Asset {\n id: string;\n url: string;\n type: string;\n is_reference?: boolean;\n /** Duration in seconds for video/audio assets */\n duration?: number;\n}\n\n// ── Timeline types ──────────────────────────────────────────────────────────\n\nexport type TimelineElementType = \"video\" | \"image\" | \"text\" | \"audio\" | \"composition\";\nexport type MediaElementType = \"video\" | \"image\" | \"audio\";\n\nexport const CANVAS_DIMENSIONS = {\n landscape: { width: 1920, height: 1080 },\n portrait: { width: 1080, height: 1920 },\n \"landscape-4k\": { width: 3840, height: 2160 },\n \"portrait-4k\": { width: 2160, height: 3840 },\n square: { width: 1080, height: 1080 },\n \"square-4k\": { width: 2160, height: 2160 },\n} as const;\n\n// Single source of truth: derive the type from the table so adding a preset\n// extends the union automatically. Avoids the prior `as readonly CanvasResolution[]`\n// cast on `VALID_CANVAS_RESOLUTIONS` quietly drifting if the table grew but\n// the union didn't.\nexport type CanvasResolution = keyof typeof CANVAS_DIMENSIONS;\n\n// `Object.keys` ordering matches insertion order in `CANVAS_DIMENSIONS` on\n// every supported JS engine; tests pin the order in `index.test.ts`. Reorder\n// the table above with care.\nexport const VALID_CANVAS_RESOLUTIONS = Object.keys(\n CANVAS_DIMENSIONS,\n) as readonly CanvasResolution[];\n\nconst RESOLUTION_ALIASES: Record<string, CanvasResolution> = {\n \"1080p\": \"landscape\",\n hd: \"landscape\",\n \"1080p-portrait\": \"portrait\",\n \"portrait-1080p\": \"portrait\",\n \"4k\": \"landscape-4k\",\n uhd: \"landscape-4k\",\n \"4k-portrait\": \"portrait-4k\",\n \"1080p-square\": \"square\",\n \"square-1080p\": \"square\",\n \"4k-square\": \"square-4k\",\n};\n\n/**\n * Aliases that name a resolution *tier* (1080p / 4K) without also nailing an\n * orientation. Historically they all normalize to the `landscape` preset\n * (`normalizeResolutionFlag(\"1080p\") === \"landscape\"`), which then rejects\n * portrait / square compositions with a cryptic \"aspect ratio does not match\"\n * error deep inside the render pipeline. Consumers that know the composition's\n * dimensions can consult this set to decide whether to auto-adapt the preset's\n * orientation instead — see `adaptAspectAgnosticResolution` in the compile\n * stage (`@hyperframes/producer`) and `suggestMatchingPreset` here.\n *\n * Orientation-suffixed aliases (`1080p-portrait`, `4k-square`, …) are absent\n * on purpose: the user *did* pick an orientation, and honoring it is important\n * for the \"you asked for landscape but composed portrait\" error to still fire.\n */\nconst ASPECT_AGNOSTIC_RESOLUTION_ALIASES: ReadonlySet<string> = new Set([\n \"1080p\",\n \"hd\",\n \"4k\",\n \"uhd\",\n]);\n\n/**\n * Map a user-facing resolution string (canonical name or alias) to a\n * `CanvasResolution`. Returns undefined for unknown values so callers\n * can produce their own \"invalid\" UX (CLI exit, route validation, etc.).\n */\nexport function normalizeResolutionFlag(input: string | undefined): CanvasResolution | undefined {\n if (!input) return undefined;\n const lowered = input.toLowerCase();\n if ((VALID_CANVAS_RESOLUTIONS as readonly string[]).includes(lowered)) {\n return lowered as CanvasResolution;\n }\n return RESOLUTION_ALIASES[lowered];\n}\n\n/**\n * True when `input` names a resolution *tier* without nailing an orientation\n * (`1080p`, `hd`, `4k`, `uhd`). Case-insensitive.\n *\n * The `--resolution` CLI flag treats these as \"target this size; keep the\n * composition's orientation\" — a portrait 1080×1920 comp with `--resolution\n * 1080p` should land at 1080×1920, not blow up on aspect mismatch. Explicit\n * canonical presets (`landscape`, `portrait`, …) and orientation-suffixed\n * aliases (`1080p-portrait`) stay strict — the user picked an orientation.\n *\n * Consumers pair this signal with the composition's dimensions (in a\n * follow-up pass after HTML parse) to pick the right preset via\n * `suggestMatchingPreset` — see `adaptAspectAgnosticResolution` in the\n * compile stage (`@hyperframes/producer`) for the canonical remap.\n */\nexport function isAspectAgnosticResolutionAlias(input: string | undefined): boolean {\n if (!input) return false;\n return ASPECT_AGNOSTIC_RESOLUTION_ALIASES.has(input.toLowerCase());\n}\n\n/**\n * Public-boundary helper: given a raw `--resolution` / `--output-resolution`\n * flag value, return the pair every distributed render entrypoint needs to\n * forward end-to-end so the compile stage can adapt aspect-agnostic aliases\n * to the composition's orientation:\n *\n * - `outputResolution`: normalized {@link CanvasResolution} (or `undefined`\n * for unknown values — callers own their invalid-input UX).\n * - `outputResolutionAspectAgnostic`: `true` when the raw input was a\n * tier-only alias (`1080p` / `hd` / `4k` / `uhd`). Passes through to\n * `DistributedRenderConfig.outputResolutionAspectAgnostic` so the compile\n * stage remaps `landscape` → `portrait` / `square` when the composition\n * dimensions demand it.\n *\n * Exported to centralize the two-step pattern (`normalizeResolutionFlag` +\n * `isAspectAgnosticResolutionAlias`) that would otherwise be duplicated at\n * every entrypoint that emits a `DistributedRenderConfig` (`hyperframes\n * cloudrun render`, `hyperframes lambda render` / `render-batch`, the local\n * CLI). Divergence between those callers is what shipped the portrait-1080p\n * regression this helper prevents from recurring.\n */\nexport interface ResolvedResolutionFlag {\n outputResolution: CanvasResolution | undefined;\n outputResolutionAspectAgnostic: boolean;\n}\n\nexport function resolveResolutionFlagPair(input: string | undefined): ResolvedResolutionFlag {\n return {\n outputResolution: normalizeResolutionFlag(input),\n outputResolutionAspectAgnostic: isAspectAgnosticResolutionAlias(input),\n };\n}\n\nexport interface TimelineElementBase {\n id: string;\n type: TimelineElementType;\n name: string;\n startTime: number;\n duration: number;\n zIndex: number;\n x?: number;\n y?: number;\n scale?: number;\n opacity?: number;\n}\n\nexport interface TimelineMediaElement extends TimelineElementBase {\n type: MediaElementType;\n src: string;\n mediaStartTime?: number;\n sourceDuration?: number;\n isAroll?: boolean;\n sourceWidth?: number;\n sourceHeight?: number;\n volume?: number; // 0-1 (0% to 100%), default 1.0\n hasAudio?: boolean; // For videos - indicates if video has audio track\n}\n\nexport interface WaveformData {\n peaks: number[];\n duration: number;\n sampleRate?: number;\n}\n\nexport interface TimelineTextElement extends TimelineElementBase {\n type: \"text\";\n content: string;\n color?: string;\n fontSize?: number;\n textShadow?: boolean;\n fontFamily?: string;\n fontWeight?: number;\n textOutline?: boolean;\n textOutlineColor?: string;\n textOutlineWidth?: number;\n textHighlight?: boolean;\n textHighlightColor?: string;\n textHighlightPadding?: number;\n textHighlightRadius?: number;\n}\n\nexport interface TimelineCompositionElement extends TimelineElementBase {\n type: \"composition\";\n src: string;\n compositionId: string;\n scale?: number;\n sourceDuration?: number;\n variableValues?: Record<string, string | number | boolean>;\n sourceWidth?: number;\n sourceHeight?: number;\n}\n\n// Composition Variable Types\nexport type CompositionVariableType =\n | \"string\"\n | \"number\"\n | \"color\"\n | \"boolean\"\n | \"enum\"\n | \"font\"\n | \"image\";\n\n/**\n * Runtime list of every valid `CompositionVariableType`. Use this anywhere\n * a Set/array of valid type strings is needed (lint rules, validators).\n * The `satisfies` guard turns adding a new variant to the union without\n * also adding it here into a compile error.\n */\nexport const COMPOSITION_VARIABLE_TYPES = [\n \"string\",\n \"number\",\n \"color\",\n \"boolean\",\n \"enum\",\n \"font\",\n \"image\",\n] as const satisfies readonly CompositionVariableType[];\n\nexport interface CompositionVariableBase {\n id: string;\n type: CompositionVariableType;\n label: string;\n description?: string;\n}\n\nexport interface StringVariable extends CompositionVariableBase {\n type: \"string\";\n default: string;\n placeholder?: string;\n maxLength?: number;\n}\n\nexport interface NumberVariable extends CompositionVariableBase {\n type: \"number\";\n default: number;\n min?: number;\n max?: number;\n step?: number;\n unit?: string;\n}\n\nexport interface ColorVariable extends CompositionVariableBase {\n type: \"color\";\n default: string;\n /** Brand role identifier, e.g. \"color:primary\". */\n brandRole?: string;\n}\n\nexport interface BooleanVariable extends CompositionVariableBase {\n type: \"boolean\";\n default: boolean;\n}\n\nexport interface EnumVariable extends CompositionVariableBase {\n type: \"enum\";\n default: string;\n options: { value: string; label: string }[];\n}\n\n/**\n * Font variable — value is a `{name, source}` object (object-valued; LOCKED §7).\n * `default` is the fallback font-family name string.\n * `source` is the font stylesheet URL (e.g. Google Fonts CSS).\n * `default_name` / `default_source` are the CSS-level fallbacks when the\n * brand font is absent.\n */\nexport interface FontVariable extends CompositionVariableBase {\n type: \"font\";\n /** Fallback font-family name, e.g. \"Inter\". */\n default: string;\n /** Font stylesheet URL (e.g. Google Fonts CSS link). */\n source?: string;\n /** CSS font-family name to use when source is unavailable, e.g. \"sans-serif\". */\n default_name?: string;\n /** Fallback font stylesheet URL (empty string = system font). */\n default_source?: string;\n}\n\n/**\n * Image variable — value is a `{url, …}` object (object-valued; LOCKED §7).\n * `default` is the fallback image URL string.\n * `brandRole` is an optional semantic label, e.g. \"logo:primary\".\n */\nexport interface ImageVariable extends CompositionVariableBase {\n type: \"image\";\n /** Fallback image URL. */\n default: string;\n /** Brand role identifier, e.g. \"logo:primary\". */\n brandRole?: string;\n}\n\nexport type CompositionVariable =\n | StringVariable\n | NumberVariable\n | ColorVariable\n | BooleanVariable\n | EnumVariable\n | FontVariable\n | ImageVariable;\n\nexport interface CompositionSpec {\n id: string;\n duration: number;\n variables: CompositionVariable[];\n}\n\nexport type TimelineElement =\n | TimelineMediaElement\n | TimelineTextElement\n | TimelineCompositionElement;\n\nexport function isTextElement(el: TimelineElement): el is TimelineTextElement {\n return el.type === \"text\";\n}\n\nexport function isMediaElement(el: TimelineElement): el is TimelineMediaElement {\n return el.type === \"video\" || el.type === \"image\" || el.type === \"audio\";\n}\n\nexport function isCompositionElement(el: TimelineElement): el is TimelineCompositionElement {\n return el.type === \"composition\";\n}\n\nexport interface MediaFile {\n id: string;\n name: string;\n type: TimelineElementType;\n src: string;\n file?: File;\n duration?: number;\n compositionId?: string;\n sourceWidth?: number; // Intrinsic width for compositions\n sourceHeight?: number; // Intrinsic height for compositions\n}\n\nexport const TIMELINE_COLORS: Record<TimelineElementType, string> = {\n video: \"#ec4899\",\n image: \"#3b82f6\",\n text: \"#06b6d4\",\n audio: \"#10b981\",\n composition: \"#f97316\",\n};\n\nexport const DEFAULT_DURATIONS: Record<TimelineElementType, number> = {\n video: 5,\n image: 5,\n text: 2,\n audio: 5,\n composition: 5,\n};\n\nexport interface CompositionAPI {\n id: string;\n duration: number;\n seek(time: number): void;\n getTime(): number;\n getDuration(): number;\n}\n\n// ── Player API types (used by runtime) ────────────────────────────────────\n\nexport interface PlayerAPI {\n play(): void;\n pause(): void;\n seek(time: number, options?: { keepPlaying?: boolean }): void;\n getTime(): number;\n getDuration(): number;\n isPlaying(): boolean;\n getMainTimeline(): unknown;\n getElementBounds(elementId: string): void;\n getElementsAtPoint(x: number, y: number): void;\n setElementPosition(elementId: string, x: number, y: number): void;\n previewElementPosition(elementId: string, x: number, y: number): void;\n setElementKeyframes(\n elementId: string,\n keyframes: Array<{\n id: string;\n time: number;\n properties: { x?: number; y?: number };\n }> | null,\n ): void;\n setElementScale(elementId: string, scale: number): void;\n setElementFontSize(elementId: string, fontSize: number): void;\n setElementTextContent(elementId: string, content: string): void;\n setElementTextColor(elementId: string, color: string): void;\n setElementTextShadow(elementId: string, enabled: boolean): void;\n setElementTextFontWeight(elementId: string, weight: number): void;\n setElementTextFontFamily(elementId: string, fontFamily: string): void;\n setElementTextOutline(elementId: string, enabled: boolean, color?: string, width?: number): void;\n setElementTextHighlight(\n elementId: string,\n enabled: boolean,\n color?: string,\n padding?: number,\n radius?: number,\n ): void;\n setElementVolume(elementId: string, volume: number): void;\n setStageZoom(scale: number, focusX: number, focusY: number): void;\n getStageZoom(): { scale: number; focusX: number; focusY: number };\n setStageZoomKeyframes(\n keyframes: Array<{\n id: string;\n time: number;\n zoom: { scale: number; focusX: number; focusY: number };\n ease?: string;\n }> | null,\n ): void;\n getStageZoomKeyframes(): Array<{\n id: string;\n time: number;\n zoom: { scale: number; focusX: number; focusY: number };\n ease?: string;\n }>;\n addElement(data: AddElementData): boolean;\n removeElement(elementId: string): boolean;\n updateElementTiming(elementId: string, start?: number, end?: number): boolean;\n setElementTiming(\n elementId: string,\n startTime: number,\n duration: number,\n mediaStartTime?: number,\n ): void;\n updateElementSrc(elementId: string, src: string): boolean;\n updateElementLayer(elementId: string, zIndex: number): boolean;\n updateElementBasePosition(elementId: string, x?: number, y?: number, scale?: number): boolean;\n markTimelineDirty(): void;\n isTimelineDirty(): boolean;\n rebuildTimeline(): void;\n ensureTimeline(): void;\n enableRenderMode(): void;\n disableRenderMode(): void;\n renderSeek(time: number, options?: { suppressEvents?: boolean }): void;\n getElementVisibility(elementId: string): { visible: boolean; opacity?: number };\n getVisibleElements(): Array<{ id: string; tagName: string; start: number; end: number }>;\n getRenderState(): {\n time: number;\n duration: number;\n isPlaying: boolean;\n renderMode: boolean;\n timelineDirty: boolean;\n };\n}\n\nexport interface AddElementData {\n id: string;\n type: \"video\" | \"image\" | \"text\" | \"audio\" | \"composition\";\n name?: string;\n src?: string;\n content?: string;\n start: number;\n end: number;\n zIndex?: number;\n x?: number;\n y?: number;\n scale?: number;\n fontSize?: number;\n color?: string;\n textShadow?: boolean;\n fontWeight?: number;\n textOutline?: boolean;\n textOutlineColor?: string;\n textOutlineWidth?: number;\n textHighlight?: boolean;\n textHighlightColor?: string;\n textHighlightPadding?: number;\n textHighlightRadius?: number;\n compositionId?: string;\n sourceWidth?: number;\n sourceHeight?: number;\n isAroll?: boolean;\n}\n\nexport interface ValidationResult {\n valid: boolean;\n errors: string[];\n warnings: string[];\n}\n\nexport interface CompositionAsset {\n id: string;\n name: string;\n type: \"composition\";\n src: string;\n duration: number;\n compositionId: string;\n thumbnail?: string;\n}\n\nexport interface Keyframe {\n id: string;\n time: number;\n properties: Partial<KeyframeProperties>;\n ease?: string;\n}\n\nexport interface KeyframeProperties {\n x: number;\n y: number;\n opacity: number;\n scale: number;\n scaleX: number;\n scaleY: number;\n rotation: number;\n width: number;\n height: number;\n}\n\nexport interface ElementKeyframes {\n elementId: string;\n keyframes: Keyframe[];\n}\n\nexport interface StageZoom {\n scale: number;\n focusX: number;\n focusY: number;\n}\n\nexport interface StageZoomKeyframe {\n id: string;\n time: number;\n zoom: StageZoom;\n ease?: string;\n}\n\nexport function getDefaultStageZoom(resolution: CanvasResolution): StageZoom {\n const { width, height } = CANVAS_DIMENSIONS[resolution];\n return {\n scale: 1,\n focusX: width / 2,\n focusY: height / 2,\n };\n}\n","/**\n * Single source of truth for the deterministic font alias map. Both the\n * producer's @font-face injector and the core lint rules import from here,\n * eliminating manual drift between the two.\n *\n * Keys are lowercase font family names. Values are canonical font slugs\n * matching CANONICAL_FONTS keys in the producer's deterministicFonts module.\n */\nexport const FONT_ALIAS_MAP = {\n // ── Canonical bundled fonts (self-referencing) ────────────────────────\n inter: \"inter\",\n montserrat: \"montserrat\",\n outfit: \"outfit\",\n nunito: \"nunito\",\n oswald: \"oswald\",\n \"league gothic\": \"league-gothic\",\n \"archivo black\": \"archivo-black\",\n \"space mono\": \"space-mono\",\n \"ibm plex mono\": \"ibm-plex-mono\",\n \"jetbrains mono\": \"jetbrains-mono\",\n \"eb garamond\": \"eb-garamond\",\n \"playfair display\": \"playfair-display\",\n \"source code pro\": \"source-code-pro\",\n \"noto sans jp\": \"noto-sans-jp\",\n roboto: \"roboto\",\n \"open sans\": \"open-sans\",\n lato: \"lato\",\n poppins: \"poppins\",\n\n // ── Common aliases → nearest canonical ────────────────────────────────\n \"helvetica neue\": \"inter\",\n helvetica: \"inter\",\n arial: \"inter\",\n \"helvetica bold\": \"inter\",\n futura: \"montserrat\",\n \"din alternate\": \"montserrat\",\n \"arial black\": \"montserrat\",\n \"bebas neue\": \"league-gothic\",\n \"courier new\": \"jetbrains-mono\",\n courier: \"jetbrains-mono\",\n garamond: \"eb-garamond\",\n \"noto sans japanese\": \"noto-sans-jp\",\n \"segoe ui\": \"roboto\",\n\n // ── macOS sans-serif system fonts → inter ─────────────────────────────\n \"sf pro\": \"inter\",\n \"sf pro display\": \"inter\",\n \"sf pro text\": \"inter\",\n \"sf pro rounded\": \"inter\",\n avenir: \"inter\",\n \"avenir next\": \"inter\",\n \"lucida grande\": \"inter\",\n geneva: \"inter\",\n optima: \"inter\",\n\n // ── Windows sans-serif system fonts → inter ───────────────────────────\n verdana: \"inter\",\n tahoma: \"inter\",\n \"trebuchet ms\": \"inter\",\n calibri: \"inter\",\n candara: \"inter\",\n corbel: \"inter\",\n \"lucida sans\": \"inter\",\n \"lucida sans unicode\": \"inter\",\n\n // ── Linux sans-serif system fonts → inter ─────────────────────────────\n \"noto sans\": \"inter\",\n \"dejavu sans\": \"inter\",\n \"liberation sans\": \"inter\",\n\n // ── Monospace system fonts → jetbrains-mono ───────────────────────────\n \"sf mono\": \"jetbrains-mono\",\n menlo: \"jetbrains-mono\",\n monaco: \"jetbrains-mono\",\n consolas: \"jetbrains-mono\",\n \"lucida console\": \"jetbrains-mono\",\n \"lucida sans typewriter\": \"jetbrains-mono\",\n \"andale mono\": \"jetbrains-mono\",\n \"dejavu sans mono\": \"jetbrains-mono\",\n \"liberation mono\": \"jetbrains-mono\",\n\n // ── Serif system fonts → eb-garamond ──────────────────────────────────\n georgia: \"eb-garamond\",\n palatino: \"eb-garamond\",\n \"palatino linotype\": \"eb-garamond\",\n \"book antiqua\": \"eb-garamond\",\n cambria: \"eb-garamond\",\n times: \"eb-garamond\",\n \"times new roman\": \"eb-garamond\",\n \"dejavu serif\": \"eb-garamond\",\n \"liberation serif\": \"eb-garamond\",\n} satisfies Readonly<Record<string, string>>;\n\nexport const FONT_ALIAS_KEYS: ReadonlySet<string> = new Set(Object.keys(FONT_ALIAS_MAP));\n\n/**\n * Human-readable display names for canonical font slugs. Used by the lint\n * rule to tell authors what their aliased font will render as.\n */\nexport const CANONICAL_FONT_DISPLAY_NAMES: Readonly<Record<string, string>> = {\n inter: \"Inter\",\n montserrat: \"Montserrat\",\n outfit: \"Outfit\",\n nunito: \"Nunito\",\n oswald: \"Oswald\",\n \"league-gothic\": \"League Gothic\",\n \"archivo-black\": \"Archivo Black\",\n \"space-mono\": \"Space Mono\",\n \"ibm-plex-mono\": \"IBM Plex Mono\",\n \"jetbrains-mono\": \"JetBrains Mono\",\n \"eb-garamond\": \"EB Garamond\",\n \"playfair-display\": \"Playfair Display\",\n \"source-code-pro\": \"Source Code Pro\",\n \"noto-sans-jp\": \"Noto Sans JP\",\n roboto: \"Roboto\",\n \"open-sans\": \"Open Sans\",\n lato: \"Lato\",\n poppins: \"Poppins\",\n};\n\n/**\n * Resolve a font alias to its canonical display name, or undefined if the\n * alias is not in the map.\n */\nexport function resolveAliasDisplayName(alias: string): string | undefined {\n const slug = (FONT_ALIAS_MAP as Record<string, string>)[alias.toLowerCase()];\n if (!slug) return undefined;\n return CANONICAL_FONT_DISPLAY_NAMES[slug];\n}\n","export function decodeUrlPathVariants(path: string): string[] {\n const variants = [path];\n try {\n const decoded = decodeURIComponent(path);\n if (decoded !== path) variants.unshift(decoded);\n } catch {\n // Malformed percent sequences may be literal filesystem names.\n }\n\n return variants;\n}\n","/**\n * Browser-safe parser for the `data-composition-variables` schema attribute.\n * Lives outside htmlParser.ts so browser consumers (SDK, Studio, lint) can\n * import it via `@hyperframes/parsers/composition` without pulling the\n * linkedom/Node HTML-parser machinery from the main entry.\n */\n\nimport type { CompositionVariable, CompositionVariableType } from \"./types.js\";\n\n/**\n * Required typeof for each variable type's `default`. For font the default is\n * the font-family name string; for image it is the fallback URL string —\n * extra metadata fields on both are optional and not validated here.\n */\nconst DEFAULT_TYPEOF: Record<CompositionVariableType, \"string\" | \"number\" | \"boolean\"> = {\n string: \"string\",\n number: \"number\",\n color: \"string\",\n boolean: \"boolean\",\n enum: \"string\",\n font: \"string\",\n image: \"string\",\n};\n\n/**\n * Scalar variable values (string/number/boolean) are the ones that flow into\n * CSS custom props and text bindings; font/image values are object-shaped.\n * Shared so the SDK's CSS-compat writes, the runtime bindings, and Studio's\n * display logic can never disagree on what \"scalar\" means.\n */\nexport function isScalarVariableValue(value: unknown): value is string | number | boolean {\n return typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\";\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null;\n}\n\nfunction isVariableType(t: unknown): t is CompositionVariableType {\n return typeof t === \"string\" && t in DEFAULT_TYPEOF;\n}\n\n/**\n * True when the value is a structurally valid variable declaration: id, label,\n * a known type, a default matching that type, and options[] for enums. The\n * same predicate parseCompositionVariables filters with — exported so writers\n * (SDK declaration ops, Studio forms) can validate before persisting.\n */\nexport function isCompositionVariable(v: unknown): v is CompositionVariable {\n if (!isRecord(v)) return false;\n if (typeof v.id !== \"string\" || typeof v.label !== \"string\") return false;\n if (!isVariableType(v.type)) return false;\n if (typeof v.default !== DEFAULT_TYPEOF[v.type]) return false;\n if (v.type === \"enum\" && !Array.isArray(v.options)) return false;\n return true;\n}\n\n/**\n * Parse the typed variable declarations from an element's\n * `data-composition-variables` attribute. Malformed entries (wrong shape,\n * unknown type, default not matching the declared type) are dropped; an\n * absent attribute, invalid JSON, or a non-array payload yields `[]`.\n */\nexport function parseCompositionVariables(htmlEl: Element): CompositionVariable[] {\n const variablesAttr = htmlEl.getAttribute(\"data-composition-variables\");\n if (!variablesAttr) {\n return [];\n }\n\n try {\n const parsed = JSON.parse(variablesAttr);\n if (!Array.isArray(parsed)) {\n return [];\n }\n return parsed.filter(isCompositionVariable);\n } catch {\n return [];\n }\n}\n","/**\n * Browser-safe static scan for composition-variable reads in script text.\n *\n * Compositions read variables by calling the runtime API — `getVariables()`\n * bare (sub-comp scoped shadow) or via `__hyperframes.getVariables()` /\n * `window.__hyperframes.getVariables()` — and there is no DOM-attribute\n * binding to scan, so \"which variables does this composition use\" can only be\n * derived from the scripts. This is a best-effort static analysis: the\n * patterns agents actually write (destructuring, member access, a single\n * alias variable) resolve to ids; anything opaque flips `scanIncomplete`\n * so consumers can present usage as a lower bound instead of a fact.\n *\n * AST nodes are handled untyped (same convention as gsapParserAcorn.ts) —\n * acorn's structural types don't survive acorn-walk's visitor signatures.\n */\n\nimport * as acorn from \"acorn\";\nimport * as acornWalk from \"acorn-walk\";\n\nexport interface VariableUsageScan {\n /** Variable ids statically read by the script, in first-seen order. */\n usedIds: string[];\n /**\n * True when the script accesses variables in a way the scan cannot resolve\n * (computed keys, rest spreads, the values object escaping into a call…) or\n * when the script fails to parse — usedIds is then a lower bound.\n */\n scanIncomplete: boolean;\n}\n\ninterface Sink {\n use(id: string): void;\n incomplete(): void;\n}\n\n// oxlint-disable no-explicit-any -- untyped acorn AST traversal, see header\n\nfunction isGetVariablesCallee(callee: any): boolean {\n if (callee?.type === \"Identifier\") return callee.name === \"getVariables\";\n if (callee?.type === \"MemberExpression\" && !callee.computed) {\n return callee.property?.type === \"Identifier\" && callee.property.name === \"getVariables\";\n }\n return false;\n}\n\n/** Collect ids from an ObjectPattern destructuring of the values object. */\n// Exhaustive AST-node classification — branchy by nature, same as gsapParserAcorn.\n// fallow-ignore-next-line complexity\nfunction collectFromObjectPattern(pattern: any, out: Sink): void {\n for (const prop of pattern.properties ?? []) {\n if (prop?.type === \"RestElement\") {\n out.incomplete();\n continue;\n }\n if (prop?.type !== \"Property\") continue;\n if (prop.computed === true) {\n out.incomplete();\n } else if (prop.key?.type === \"Identifier\") {\n out.use(String(prop.key.name));\n } else if (prop.key?.type === \"Literal\" && typeof prop.key.value === \"string\") {\n out.use(prop.key.value);\n } else {\n out.incomplete();\n }\n }\n}\n\n/** Collect an id from a MemberExpression reading the values object. */\nfunction collectFromMemberAccess(member: any, out: Sink): void {\n if (member.computed !== true && member.property?.type === \"Identifier\") {\n out.use(String(member.property.name));\n } else if (\n member.computed === true &&\n member.property?.type === \"Literal\" &&\n typeof member.property.value === \"string\"\n ) {\n out.use(member.property.value);\n } else {\n out.incomplete();\n }\n}\n\n/**\n * Classify one read of the values object (a getVariables() call result or an\n * alias holding it) by its immediate syntactic context. Returns the alias\n * name when the value is bound to a plain variable (`const vars = …`).\n */\n// fallow-ignore-next-line complexity\nfunction classifyValueRead(parent: any, valueNode: any, out: Sink): string | null {\n if (!parent || parent.type === \"ExpressionStatement\") {\n // Bare statement — value unused, nothing read.\n return null;\n }\n if (parent.type === \"MemberExpression\" && parent.object === valueNode) {\n collectFromMemberAccess(parent, out);\n return null;\n }\n if (parent.type === \"VariableDeclarator\" && parent.init === valueNode) {\n if (parent.id?.type === \"ObjectPattern\") {\n collectFromObjectPattern(parent.id, out);\n return null;\n }\n if (parent.id?.type === \"Identifier\") return String(parent.id.name);\n out.incomplete();\n return null;\n }\n // The values object escapes (argument, return, spread, assignment…) —\n // reads beyond this point are invisible to the scan.\n out.incomplete();\n return null;\n}\n\nexport function scanVariableUsage(scriptText: string): VariableUsageScan {\n const usedIds: string[] = [];\n const seen = new Set<string>();\n let scanIncomplete = false;\n\n const sink: Sink = {\n use(id: string) {\n if (!seen.has(id)) {\n seen.add(id);\n usedIds.push(id);\n }\n },\n incomplete() {\n scanIncomplete = true;\n },\n };\n\n let ast: any;\n try {\n ast = acorn.parse(scriptText, { ecmaVersion: \"latest\", sourceType: \"script\" });\n } catch {\n return { usedIds: [], scanIncomplete: true };\n }\n\n const aliases = new Set<string>();\n\n // Pass 1: classify every getVariables() call by its parent context.\n acornWalk.ancestor(ast, {\n CallExpression(node: any, _: unknown, ancestors: any[]) {\n if (!isGetVariablesCallee(node.callee)) return;\n const parent = ancestors[ancestors.length - 2];\n const alias = classifyValueRead(parent, node, sink);\n if (alias) aliases.add(alias);\n },\n } as any);\n\n // Pass 2: classify every reference to an alias of the values object.\n // Scope-naive by design: an unrelated same-named identifier can only make\n // the scan report extra ids or flip scanIncomplete, never miss a read.\n if (aliases.size > 0) {\n acornWalk.ancestor(ast, {\n // fallow-ignore-next-line complexity\n Identifier(node: any, _: unknown, ancestors: any[]) {\n if (!aliases.has(String(node.name))) return;\n const parent = ancestors[ancestors.length - 2];\n if (!parent) return;\n // Skip the declarator that introduced the alias and property-position\n // identifiers that merely share the name.\n if (parent.type === \"VariableDeclarator\" && parent.id === node) return;\n if (parent.type === \"MemberExpression\" && parent.property === node) return;\n if (parent.type === \"Property\" && parent.key === node && parent.computed !== true) return;\n // Chained aliases (const v2 = vars) are not followed — flag instead\n // of silently missing reads through the second name.\n if (classifyValueRead(parent, node, sink)) sink.incomplete();\n },\n } as any);\n }\n\n return { usedIds, scanIncomplete };\n}\n","/**\n * Browser-safe authored composition contract.\n *\n * Source HTML is authored with data-start + data-duration + data-track-index.\n * data-end is compiler-derived and data-layer is legacy input only. Readers\n * accept legacy documents; writers always emit the canonical representation.\n */\n\nexport const COMPOSITION_CONTRACT_VERSION = 1 as const;\n\nexport const COMPOSITION_ATTRIBUTES = Object.freeze({\n start: \"data-start\",\n duration: \"data-duration\",\n trackIndex: \"data-track-index\",\n derivedEnd: \"data-end\",\n legacyTrack: \"data-layer\",\n} as const);\n\nexport const CANONICAL_AUTHORED_TIMING_ATTRIBUTES = Object.freeze([\n COMPOSITION_ATTRIBUTES.start,\n COMPOSITION_ATTRIBUTES.duration,\n COMPOSITION_ATTRIBUTES.trackIndex,\n] as const);\n\nexport const DERIVED_TIMING_ATTRIBUTES = Object.freeze([\n COMPOSITION_ATTRIBUTES.derivedEnd,\n] as const);\n\nexport const LEGACY_TIMING_ATTRIBUTES = Object.freeze([\n COMPOSITION_ATTRIBUTES.derivedEnd,\n COMPOSITION_ATTRIBUTES.legacyTrack,\n] as const);\n\nexport type ReferenceExpression =\n | { kind: \"absolute\"; value: number }\n | { kind: \"reference\"; refId: string; offset: number };\n\nexport type ClipTimingDiagnosticCode =\n | \"invalid-start\"\n | \"unresolved-start-reference\"\n | \"invalid-duration\"\n | \"invalid-end\"\n | \"end-before-start\"\n | \"deprecated-end\"\n | \"conflicting-end\"\n | \"invalid-track-index\"\n | \"deprecated-layer\"\n | \"conflicting-layer\";\n\nexport interface ClipTimingDiagnostic {\n code: ClipTimingDiagnosticCode;\n attribute: string;\n value: string | null;\n}\n\nexport interface ClipAttributeReader {\n getAttribute(name: string): string | null;\n}\n\nexport interface ClipAttributeWriter extends ClipAttributeReader {\n setAttribute(name: string, value: string): void;\n removeAttribute(name: string): void;\n}\n\nexport interface ReadClipTimingOptions {\n /** Resolve a reference to the referenced clip's absolute end time. */\n resolveReferenceEnd?: (refId: string) => number | null | undefined;\n /** Start used when data-start is absent. Defaults to zero. */\n defaultStart?: number | null;\n}\n\nexport interface ClipTiming {\n startExpression: ReferenceExpression | null;\n start: number | null;\n duration: number | null;\n end: number | null;\n trackIndex: number;\n durationSource: \"duration\" | \"legacy-end\" | \"missing\" | \"invalid\";\n trackSource: \"track-index\" | \"legacy-layer\" | \"default\" | \"invalid\";\n diagnostics: ClipTimingDiagnostic[];\n}\n\nexport interface ClipTimingUpdate {\n start?: number | string | ReferenceExpression;\n duration?: number | null;\n trackIndex?: number | null;\n}\n\nexport class ClipTimingWriteError extends Error {\n readonly code: \"invalid-start\" | \"invalid-duration\" | \"invalid-track-index\";\n\n constructor(code: ClipTimingWriteError[\"code\"], message: string) {\n super(message);\n this.name = \"ClipTimingWriteError\";\n this.code = code;\n }\n}\n\n/** Parse a value to a finite number, or null if it is absent/invalid. */\nexport function parseNumeric(value: string | null | undefined): number | null {\n if (value == null || value.trim() === \"\") return null;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : null;\n}\n\nconst REFERENCE_ID_PATTERN = /^[A-Za-z0-9_.:-]+$/;\n\nfunction isAsciiDigitAt(value: string, index: number): boolean {\n const code = value.charCodeAt(index);\n return code >= 48 && code <= 57;\n}\n\nfunction skipDigitsLeft(value: string, start: number): number {\n let cursor = start;\n while (cursor >= 0 && isAsciiDigitAt(value, cursor)) cursor--;\n return cursor;\n}\n\nfunction skipWhitespaceLeft(value: string, start: number): number {\n let cursor = start;\n while (cursor >= 0 && (value[cursor] ?? \"\").trim() === \"\") cursor--;\n return cursor;\n}\n\nfunction findMagnitudeStart(value: string): number | null {\n const last = value.length - 1;\n if (!isAsciiDigitAt(value, last)) return null;\n let cursor = skipDigitsLeft(value, last);\n if (value[cursor] === \".\") cursor = skipDigitsLeft(value, cursor - 1);\n return cursor + 1;\n}\n\nfunction parseReferenceOffset(\n value: string,\n): { refId: string; operator: \"+\" | \"-\"; magnitude: number } | null {\n const magnitudeStart = findMagnitudeStart(value);\n if (magnitudeStart == null) return null;\n const operatorIndex = skipWhitespaceLeft(value, magnitudeStart - 1);\n const operator = value[operatorIndex];\n if (operator !== \"+\" && operator !== \"-\") return null;\n\n const refId = value.slice(0, operatorIndex).trim();\n if (!REFERENCE_ID_PATTERN.test(refId)) return null;\n const magnitude = Number(value.slice(magnitudeStart));\n if (!Number.isFinite(magnitude)) return null;\n return { refId, operator, magnitude };\n}\n\n/**\n * Parse the data-start grammar: absolute seconds, `clip-id`, or\n * `clip-id +/- offset`, where references resolve to the referenced clip's end.\n */\nexport function parseStartExpression(raw: string | null | undefined): ReferenceExpression | null {\n const normalized = (raw ?? \"\").trim();\n if (!normalized) return null;\n const absolute = parseNumeric(normalized);\n if (absolute != null) return { kind: \"absolute\", value: absolute };\n if (REFERENCE_ID_PATTERN.test(normalized)) {\n return { kind: \"reference\", refId: normalized, offset: 0 };\n }\n const reference = parseReferenceOffset(normalized);\n if (!reference) return null;\n return {\n kind: \"reference\",\n refId: reference.refId,\n offset: reference.operator === \"-\" ? -reference.magnitude : reference.magnitude,\n };\n}\n\nfunction pushDiagnostic(\n diagnostics: ClipTimingDiagnostic[],\n code: ClipTimingDiagnosticCode,\n attribute: string,\n value: string | null,\n): void {\n diagnostics.push({ code, attribute, value });\n}\n\nfunction resolveStart(\n expression: ReferenceExpression | null,\n rawStart: string | null,\n options: ReadClipTimingOptions,\n diagnostics: ClipTimingDiagnostic[],\n): number | null {\n if (rawStart == null || rawStart.trim() === \"\") {\n return options.defaultStart === undefined ? 0 : options.defaultStart;\n }\n if (!expression) {\n pushDiagnostic(diagnostics, \"invalid-start\", COMPOSITION_ATTRIBUTES.start, rawStart);\n return null;\n }\n if (expression.kind === \"absolute\") return Math.max(0, expression.value);\n const referencedEnd = options.resolveReferenceEnd?.(expression.refId);\n if (referencedEnd == null || !Number.isFinite(referencedEnd)) {\n pushDiagnostic(\n diagnostics,\n \"unresolved-start-reference\",\n COMPOSITION_ATTRIBUTES.start,\n rawStart,\n );\n return null;\n }\n return Math.max(0, referencedEnd + expression.offset);\n}\n\ntype DurationRead = Pick<ClipTiming, \"duration\" | \"end\" | \"durationSource\">;\ntype TrackRead = Pick<ClipTiming, \"trackIndex\" | \"trackSource\">;\n\nfunction diagnoseDerivedEnd(\n rawEnd: string,\n canonicalEnd: number | null,\n diagnostics: ClipTimingDiagnostic[],\n): void {\n pushDiagnostic(diagnostics, \"deprecated-end\", COMPOSITION_ATTRIBUTES.derivedEnd, rawEnd);\n const parsedEnd = parseNumeric(rawEnd);\n if (parsedEnd == null) {\n pushDiagnostic(diagnostics, \"invalid-end\", COMPOSITION_ATTRIBUTES.derivedEnd, rawEnd);\n } else if (canonicalEnd != null && parsedEnd !== canonicalEnd) {\n pushDiagnostic(diagnostics, \"conflicting-end\", COMPOSITION_ATTRIBUTES.derivedEnd, rawEnd);\n }\n}\n\nfunction readCanonicalDuration(\n rawDuration: string,\n start: number | null,\n diagnostics: ClipTimingDiagnostic[],\n): DurationRead {\n const duration = parseNumeric(rawDuration);\n if (duration == null || duration < 0) {\n pushDiagnostic(diagnostics, \"invalid-duration\", COMPOSITION_ATTRIBUTES.duration, rawDuration);\n return { duration: null, end: null, durationSource: \"invalid\" };\n }\n return {\n duration,\n end: start == null ? null : start + duration,\n durationSource: \"duration\",\n };\n}\n\nfunction readLegacyEnd(\n rawEnd: string,\n start: number | null,\n diagnostics: ClipTimingDiagnostic[],\n): DurationRead {\n pushDiagnostic(diagnostics, \"deprecated-end\", COMPOSITION_ATTRIBUTES.derivedEnd, rawEnd);\n const end = parseNumeric(rawEnd);\n if (end == null) {\n pushDiagnostic(diagnostics, \"invalid-end\", COMPOSITION_ATTRIBUTES.derivedEnd, rawEnd);\n return { duration: null, end: null, durationSource: \"invalid\" };\n }\n if (start == null) return { duration: null, end, durationSource: \"legacy-end\" };\n if (end < start) {\n pushDiagnostic(diagnostics, \"end-before-start\", COMPOSITION_ATTRIBUTES.derivedEnd, rawEnd);\n return { duration: null, end: null, durationSource: \"invalid\" };\n }\n return { duration: end - start, end, durationSource: \"legacy-end\" };\n}\n\nfunction readDuration(\n attributes: ClipAttributeReader,\n start: number | null,\n diagnostics: ClipTimingDiagnostic[],\n): DurationRead {\n const rawDuration = attributes.getAttribute(COMPOSITION_ATTRIBUTES.duration);\n const rawEnd = attributes.getAttribute(COMPOSITION_ATTRIBUTES.derivedEnd);\n if (rawDuration == null) {\n return rawEnd == null\n ? { duration: null, end: null, durationSource: \"missing\" }\n : readLegacyEnd(rawEnd, start, diagnostics);\n }\n const canonical = readCanonicalDuration(rawDuration, start, diagnostics);\n if (rawEnd != null) diagnoseDerivedEnd(rawEnd, canonical.end, diagnostics);\n return canonical;\n}\n\nfunction readTrackValue(\n rawValue: string,\n attribute: string,\n source: \"track-index\" | \"legacy-layer\",\n diagnostics: ClipTimingDiagnostic[],\n): TrackRead {\n const trackIndex = parseNumeric(rawValue);\n if (trackIndex == null || !Number.isInteger(trackIndex)) {\n pushDiagnostic(diagnostics, \"invalid-track-index\", attribute, rawValue);\n return { trackIndex: 0, trackSource: \"invalid\" };\n }\n return { trackIndex, trackSource: source };\n}\n\nfunction readTrack(\n attributes: ClipAttributeReader,\n diagnostics: ClipTimingDiagnostic[],\n): TrackRead {\n const rawTrack = attributes.getAttribute(COMPOSITION_ATTRIBUTES.trackIndex);\n const rawLayer = attributes.getAttribute(COMPOSITION_ATTRIBUTES.legacyTrack);\n if (rawTrack == null && rawLayer == null) return { trackIndex: 0, trackSource: \"default\" };\n if (rawTrack == null && rawLayer != null) {\n pushDiagnostic(diagnostics, \"deprecated-layer\", COMPOSITION_ATTRIBUTES.legacyTrack, rawLayer);\n return readTrackValue(\n rawLayer,\n COMPOSITION_ATTRIBUTES.legacyTrack,\n \"legacy-layer\",\n diagnostics,\n );\n }\n const canonical = readTrackValue(\n rawTrack ?? \"\",\n COMPOSITION_ATTRIBUTES.trackIndex,\n \"track-index\",\n diagnostics,\n );\n if (rawLayer == null) return canonical;\n pushDiagnostic(diagnostics, \"deprecated-layer\", COMPOSITION_ATTRIBUTES.legacyTrack, rawLayer);\n const parsedLayer = parseNumeric(rawLayer);\n if (parsedLayer != null && parsedLayer !== canonical.trackIndex) {\n pushDiagnostic(diagnostics, \"conflicting-layer\", COMPOSITION_ATTRIBUTES.legacyTrack, rawLayer);\n }\n return canonical;\n}\n\n/** Read canonical timing, accepting legacy attributes without preferring them. */\nexport function readClipTiming(\n attributes: ClipAttributeReader,\n options: ReadClipTimingOptions = {},\n): ClipTiming {\n const diagnostics: ClipTimingDiagnostic[] = [];\n const rawStart = attributes.getAttribute(COMPOSITION_ATTRIBUTES.start);\n const startExpression = parseStartExpression(rawStart);\n const start = resolveStart(startExpression, rawStart, options, diagnostics);\n const duration = readDuration(attributes, start, diagnostics);\n const track = readTrack(attributes, diagnostics);\n return { startExpression, start, ...duration, ...track, diagnostics };\n}\n\nfunction serializeStartNumber(start: number): string {\n if (!Number.isFinite(start)) {\n throw new ClipTimingWriteError(\"invalid-start\", \"start must be finite\");\n }\n return String(start);\n}\n\nfunction serializeStartReference(start: ReferenceExpression): string {\n if (start.kind === \"absolute\") return serializeStartNumber(start.value);\n if (!start.refId || !Number.isFinite(start.offset)) {\n throw new ClipTimingWriteError(\n \"invalid-start\",\n \"reference start must have an id and finite offset\",\n );\n }\n if (start.offset === 0) return start.refId;\n return `${start.refId} ${start.offset < 0 ? \"-\" : \"+\"} ${Math.abs(start.offset)}`;\n}\n\nfunction serializeStart(start: ClipTimingUpdate[\"start\"]): string {\n if (typeof start === \"number\") return serializeStartNumber(start);\n if (typeof start === \"object\" && start != null) return serializeStartReference(start);\n if (typeof start === \"string\" && parseStartExpression(start)) return start.trim();\n throw new ClipTimingWriteError(\"invalid-start\", `invalid start expression: ${start ?? \"\"}`);\n}\n\nfunction writeDuration(\n attributes: ClipAttributeWriter,\n duration: number | null | undefined,\n current: ClipTiming,\n): void {\n if (duration === null) {\n attributes.removeAttribute(COMPOSITION_ATTRIBUTES.duration);\n return;\n }\n if (duration === undefined) {\n if (\n attributes.getAttribute(COMPOSITION_ATTRIBUTES.duration) == null &&\n current.duration != null\n ) {\n attributes.setAttribute(COMPOSITION_ATTRIBUTES.duration, String(current.duration));\n }\n return;\n }\n if (!Number.isFinite(duration) || duration < 0) {\n throw new ClipTimingWriteError(\n \"invalid-duration\",\n \"duration must be a finite, non-negative number\",\n );\n }\n attributes.setAttribute(COMPOSITION_ATTRIBUTES.duration, String(duration));\n}\n\nfunction writeTrack(\n attributes: ClipAttributeWriter,\n trackIndex: number | null | undefined,\n current: ClipTiming,\n): void {\n if (trackIndex === null) {\n attributes.removeAttribute(COMPOSITION_ATTRIBUTES.trackIndex);\n return;\n }\n if (trackIndex === undefined) {\n if (\n attributes.getAttribute(COMPOSITION_ATTRIBUTES.trackIndex) == null &&\n current.trackSource === \"legacy-layer\"\n ) {\n attributes.setAttribute(COMPOSITION_ATTRIBUTES.trackIndex, String(current.trackIndex));\n }\n return;\n }\n if (!Number.isFinite(trackIndex) || !Number.isInteger(trackIndex)) {\n throw new ClipTimingWriteError(\"invalid-track-index\", \"trackIndex must be a finite integer\");\n }\n attributes.setAttribute(COMPOSITION_ATTRIBUTES.trackIndex, String(trackIndex));\n}\n\n/** Write only canonical authored timing and remove derived/legacy source attributes. */\nexport function writeClipTiming(\n attributes: ClipAttributeWriter,\n update: ClipTimingUpdate,\n): ClipTiming {\n const current = readClipTiming(attributes);\n if (update.start !== undefined) {\n attributes.setAttribute(COMPOSITION_ATTRIBUTES.start, serializeStart(update.start));\n }\n writeDuration(attributes, update.duration, current);\n writeTrack(attributes, update.trackIndex, current);\n // A legacy end paired with an unresolved reference start cannot be converted\n // to data-duration without a reference resolver. On a duration-omitting edit\n // (for example, moving only the track), preserve that sole duration source\n // instead of canonicalizing it into data loss.\n const preserveUnresolvedLegacyEnd =\n update.duration === undefined &&\n attributes.getAttribute(COMPOSITION_ATTRIBUTES.duration) == null &&\n attributes.getAttribute(COMPOSITION_ATTRIBUTES.derivedEnd) != null &&\n current.duration == null;\n if (!preserveUnresolvedLegacyEnd) {\n attributes.removeAttribute(COMPOSITION_ATTRIBUTES.derivedEnd);\n }\n attributes.removeAttribute(COMPOSITION_ATTRIBUTES.legacyTrack);\n return readClipTiming(attributes);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-safe authored composition contract.
|
|
3
|
+
*
|
|
4
|
+
* Source HTML is authored with data-start + data-duration + data-track-index.
|
|
5
|
+
* data-end is compiler-derived and data-layer is legacy input only. Readers
|
|
6
|
+
* accept legacy documents; writers always emit the canonical representation.
|
|
7
|
+
*/
|
|
8
|
+
declare const COMPOSITION_CONTRACT_VERSION: 1;
|
|
9
|
+
declare const COMPOSITION_ATTRIBUTES: Readonly<{
|
|
10
|
+
readonly start: "data-start";
|
|
11
|
+
readonly duration: "data-duration";
|
|
12
|
+
readonly trackIndex: "data-track-index";
|
|
13
|
+
readonly derivedEnd: "data-end";
|
|
14
|
+
readonly legacyTrack: "data-layer";
|
|
15
|
+
}>;
|
|
16
|
+
declare const CANONICAL_AUTHORED_TIMING_ATTRIBUTES: readonly ["data-start", "data-duration", "data-track-index"];
|
|
17
|
+
declare const DERIVED_TIMING_ATTRIBUTES: readonly ["data-end"];
|
|
18
|
+
declare const LEGACY_TIMING_ATTRIBUTES: readonly ["data-end", "data-layer"];
|
|
19
|
+
type ReferenceExpression = {
|
|
20
|
+
kind: "absolute";
|
|
21
|
+
value: number;
|
|
22
|
+
} | {
|
|
23
|
+
kind: "reference";
|
|
24
|
+
refId: string;
|
|
25
|
+
offset: number;
|
|
26
|
+
};
|
|
27
|
+
type ClipTimingDiagnosticCode = "invalid-start" | "unresolved-start-reference" | "invalid-duration" | "invalid-end" | "end-before-start" | "deprecated-end" | "conflicting-end" | "invalid-track-index" | "deprecated-layer" | "conflicting-layer";
|
|
28
|
+
interface ClipTimingDiagnostic {
|
|
29
|
+
code: ClipTimingDiagnosticCode;
|
|
30
|
+
attribute: string;
|
|
31
|
+
value: string | null;
|
|
32
|
+
}
|
|
33
|
+
interface ClipAttributeReader {
|
|
34
|
+
getAttribute(name: string): string | null;
|
|
35
|
+
}
|
|
36
|
+
interface ClipAttributeWriter extends ClipAttributeReader {
|
|
37
|
+
setAttribute(name: string, value: string): void;
|
|
38
|
+
removeAttribute(name: string): void;
|
|
39
|
+
}
|
|
40
|
+
interface ReadClipTimingOptions {
|
|
41
|
+
/** Resolve a reference to the referenced clip's absolute end time. */
|
|
42
|
+
resolveReferenceEnd?: (refId: string) => number | null | undefined;
|
|
43
|
+
/** Start used when data-start is absent. Defaults to zero. */
|
|
44
|
+
defaultStart?: number | null;
|
|
45
|
+
}
|
|
46
|
+
interface ClipTiming {
|
|
47
|
+
startExpression: ReferenceExpression | null;
|
|
48
|
+
start: number | null;
|
|
49
|
+
duration: number | null;
|
|
50
|
+
end: number | null;
|
|
51
|
+
trackIndex: number;
|
|
52
|
+
durationSource: "duration" | "legacy-end" | "missing" | "invalid";
|
|
53
|
+
trackSource: "track-index" | "legacy-layer" | "default" | "invalid";
|
|
54
|
+
diagnostics: ClipTimingDiagnostic[];
|
|
55
|
+
}
|
|
56
|
+
interface ClipTimingUpdate {
|
|
57
|
+
start?: number | string | ReferenceExpression;
|
|
58
|
+
duration?: number | null;
|
|
59
|
+
trackIndex?: number | null;
|
|
60
|
+
}
|
|
61
|
+
declare class ClipTimingWriteError extends Error {
|
|
62
|
+
readonly code: "invalid-start" | "invalid-duration" | "invalid-track-index";
|
|
63
|
+
constructor(code: ClipTimingWriteError["code"], message: string);
|
|
64
|
+
}
|
|
65
|
+
/** Parse a value to a finite number, or null if it is absent/invalid. */
|
|
66
|
+
declare function parseNumeric(value: string | null | undefined): number | null;
|
|
67
|
+
/**
|
|
68
|
+
* Parse the data-start grammar: absolute seconds, `clip-id`, or
|
|
69
|
+
* `clip-id +/- offset`, where references resolve to the referenced clip's end.
|
|
70
|
+
*/
|
|
71
|
+
declare function parseStartExpression(raw: string | null | undefined): ReferenceExpression | null;
|
|
72
|
+
/** Read canonical timing, accepting legacy attributes without preferring them. */
|
|
73
|
+
declare function readClipTiming(attributes: ClipAttributeReader, options?: ReadClipTimingOptions): ClipTiming;
|
|
74
|
+
/** Write only canonical authored timing and remove derived/legacy source attributes. */
|
|
75
|
+
declare function writeClipTiming(attributes: ClipAttributeWriter, update: ClipTimingUpdate): ClipTiming;
|
|
76
|
+
|
|
77
|
+
export { CANONICAL_AUTHORED_TIMING_ATTRIBUTES, COMPOSITION_ATTRIBUTES, COMPOSITION_CONTRACT_VERSION, type ClipAttributeReader, type ClipAttributeWriter, type ClipTiming, type ClipTimingDiagnostic, type ClipTimingDiagnosticCode, type ClipTimingUpdate, ClipTimingWriteError, DERIVED_TIMING_ATTRIBUTES, LEGACY_TIMING_ATTRIBUTES, type ReadClipTimingOptions, type ReferenceExpression, parseNumeric, parseStartExpression, readClipTiming, writeClipTiming };
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
// src/compositionContract.ts
|
|
2
|
+
var COMPOSITION_CONTRACT_VERSION = 1;
|
|
3
|
+
var COMPOSITION_ATTRIBUTES = Object.freeze({
|
|
4
|
+
start: "data-start",
|
|
5
|
+
duration: "data-duration",
|
|
6
|
+
trackIndex: "data-track-index",
|
|
7
|
+
derivedEnd: "data-end",
|
|
8
|
+
legacyTrack: "data-layer"
|
|
9
|
+
});
|
|
10
|
+
var CANONICAL_AUTHORED_TIMING_ATTRIBUTES = Object.freeze([
|
|
11
|
+
COMPOSITION_ATTRIBUTES.start,
|
|
12
|
+
COMPOSITION_ATTRIBUTES.duration,
|
|
13
|
+
COMPOSITION_ATTRIBUTES.trackIndex
|
|
14
|
+
]);
|
|
15
|
+
var DERIVED_TIMING_ATTRIBUTES = Object.freeze([
|
|
16
|
+
COMPOSITION_ATTRIBUTES.derivedEnd
|
|
17
|
+
]);
|
|
18
|
+
var LEGACY_TIMING_ATTRIBUTES = Object.freeze([
|
|
19
|
+
COMPOSITION_ATTRIBUTES.derivedEnd,
|
|
20
|
+
COMPOSITION_ATTRIBUTES.legacyTrack
|
|
21
|
+
]);
|
|
22
|
+
var ClipTimingWriteError = class extends Error {
|
|
23
|
+
code;
|
|
24
|
+
constructor(code, message) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = "ClipTimingWriteError";
|
|
27
|
+
this.code = code;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
function parseNumeric(value) {
|
|
31
|
+
if (value == null || value.trim() === "") return null;
|
|
32
|
+
const parsed = Number(value);
|
|
33
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
34
|
+
}
|
|
35
|
+
var REFERENCE_ID_PATTERN = /^[A-Za-z0-9_.:-]+$/;
|
|
36
|
+
function isAsciiDigitAt(value, index) {
|
|
37
|
+
const code = value.charCodeAt(index);
|
|
38
|
+
return code >= 48 && code <= 57;
|
|
39
|
+
}
|
|
40
|
+
function skipDigitsLeft(value, start) {
|
|
41
|
+
let cursor = start;
|
|
42
|
+
while (cursor >= 0 && isAsciiDigitAt(value, cursor)) cursor--;
|
|
43
|
+
return cursor;
|
|
44
|
+
}
|
|
45
|
+
function skipWhitespaceLeft(value, start) {
|
|
46
|
+
let cursor = start;
|
|
47
|
+
while (cursor >= 0 && (value[cursor] ?? "").trim() === "") cursor--;
|
|
48
|
+
return cursor;
|
|
49
|
+
}
|
|
50
|
+
function findMagnitudeStart(value) {
|
|
51
|
+
const last = value.length - 1;
|
|
52
|
+
if (!isAsciiDigitAt(value, last)) return null;
|
|
53
|
+
let cursor = skipDigitsLeft(value, last);
|
|
54
|
+
if (value[cursor] === ".") cursor = skipDigitsLeft(value, cursor - 1);
|
|
55
|
+
return cursor + 1;
|
|
56
|
+
}
|
|
57
|
+
function parseReferenceOffset(value) {
|
|
58
|
+
const magnitudeStart = findMagnitudeStart(value);
|
|
59
|
+
if (magnitudeStart == null) return null;
|
|
60
|
+
const operatorIndex = skipWhitespaceLeft(value, magnitudeStart - 1);
|
|
61
|
+
const operator = value[operatorIndex];
|
|
62
|
+
if (operator !== "+" && operator !== "-") return null;
|
|
63
|
+
const refId = value.slice(0, operatorIndex).trim();
|
|
64
|
+
if (!REFERENCE_ID_PATTERN.test(refId)) return null;
|
|
65
|
+
const magnitude = Number(value.slice(magnitudeStart));
|
|
66
|
+
if (!Number.isFinite(magnitude)) return null;
|
|
67
|
+
return { refId, operator, magnitude };
|
|
68
|
+
}
|
|
69
|
+
function parseStartExpression(raw) {
|
|
70
|
+
const normalized = (raw ?? "").trim();
|
|
71
|
+
if (!normalized) return null;
|
|
72
|
+
const absolute = parseNumeric(normalized);
|
|
73
|
+
if (absolute != null) return { kind: "absolute", value: absolute };
|
|
74
|
+
if (REFERENCE_ID_PATTERN.test(normalized)) {
|
|
75
|
+
return { kind: "reference", refId: normalized, offset: 0 };
|
|
76
|
+
}
|
|
77
|
+
const reference = parseReferenceOffset(normalized);
|
|
78
|
+
if (!reference) return null;
|
|
79
|
+
return {
|
|
80
|
+
kind: "reference",
|
|
81
|
+
refId: reference.refId,
|
|
82
|
+
offset: reference.operator === "-" ? -reference.magnitude : reference.magnitude
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function pushDiagnostic(diagnostics, code, attribute, value) {
|
|
86
|
+
diagnostics.push({ code, attribute, value });
|
|
87
|
+
}
|
|
88
|
+
function resolveStart(expression, rawStart, options, diagnostics) {
|
|
89
|
+
if (rawStart == null || rawStart.trim() === "") {
|
|
90
|
+
return options.defaultStart === void 0 ? 0 : options.defaultStart;
|
|
91
|
+
}
|
|
92
|
+
if (!expression) {
|
|
93
|
+
pushDiagnostic(diagnostics, "invalid-start", COMPOSITION_ATTRIBUTES.start, rawStart);
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
if (expression.kind === "absolute") return Math.max(0, expression.value);
|
|
97
|
+
const referencedEnd = options.resolveReferenceEnd?.(expression.refId);
|
|
98
|
+
if (referencedEnd == null || !Number.isFinite(referencedEnd)) {
|
|
99
|
+
pushDiagnostic(
|
|
100
|
+
diagnostics,
|
|
101
|
+
"unresolved-start-reference",
|
|
102
|
+
COMPOSITION_ATTRIBUTES.start,
|
|
103
|
+
rawStart
|
|
104
|
+
);
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
return Math.max(0, referencedEnd + expression.offset);
|
|
108
|
+
}
|
|
109
|
+
function diagnoseDerivedEnd(rawEnd, canonicalEnd, diagnostics) {
|
|
110
|
+
pushDiagnostic(diagnostics, "deprecated-end", COMPOSITION_ATTRIBUTES.derivedEnd, rawEnd);
|
|
111
|
+
const parsedEnd = parseNumeric(rawEnd);
|
|
112
|
+
if (parsedEnd == null) {
|
|
113
|
+
pushDiagnostic(diagnostics, "invalid-end", COMPOSITION_ATTRIBUTES.derivedEnd, rawEnd);
|
|
114
|
+
} else if (canonicalEnd != null && parsedEnd !== canonicalEnd) {
|
|
115
|
+
pushDiagnostic(diagnostics, "conflicting-end", COMPOSITION_ATTRIBUTES.derivedEnd, rawEnd);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function readCanonicalDuration(rawDuration, start, diagnostics) {
|
|
119
|
+
const duration = parseNumeric(rawDuration);
|
|
120
|
+
if (duration == null || duration < 0) {
|
|
121
|
+
pushDiagnostic(diagnostics, "invalid-duration", COMPOSITION_ATTRIBUTES.duration, rawDuration);
|
|
122
|
+
return { duration: null, end: null, durationSource: "invalid" };
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
duration,
|
|
126
|
+
end: start == null ? null : start + duration,
|
|
127
|
+
durationSource: "duration"
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function readLegacyEnd(rawEnd, start, diagnostics) {
|
|
131
|
+
pushDiagnostic(diagnostics, "deprecated-end", COMPOSITION_ATTRIBUTES.derivedEnd, rawEnd);
|
|
132
|
+
const end = parseNumeric(rawEnd);
|
|
133
|
+
if (end == null) {
|
|
134
|
+
pushDiagnostic(diagnostics, "invalid-end", COMPOSITION_ATTRIBUTES.derivedEnd, rawEnd);
|
|
135
|
+
return { duration: null, end: null, durationSource: "invalid" };
|
|
136
|
+
}
|
|
137
|
+
if (start == null) return { duration: null, end, durationSource: "legacy-end" };
|
|
138
|
+
if (end < start) {
|
|
139
|
+
pushDiagnostic(diagnostics, "end-before-start", COMPOSITION_ATTRIBUTES.derivedEnd, rawEnd);
|
|
140
|
+
return { duration: null, end: null, durationSource: "invalid" };
|
|
141
|
+
}
|
|
142
|
+
return { duration: end - start, end, durationSource: "legacy-end" };
|
|
143
|
+
}
|
|
144
|
+
function readDuration(attributes, start, diagnostics) {
|
|
145
|
+
const rawDuration = attributes.getAttribute(COMPOSITION_ATTRIBUTES.duration);
|
|
146
|
+
const rawEnd = attributes.getAttribute(COMPOSITION_ATTRIBUTES.derivedEnd);
|
|
147
|
+
if (rawDuration == null) {
|
|
148
|
+
return rawEnd == null ? { duration: null, end: null, durationSource: "missing" } : readLegacyEnd(rawEnd, start, diagnostics);
|
|
149
|
+
}
|
|
150
|
+
const canonical = readCanonicalDuration(rawDuration, start, diagnostics);
|
|
151
|
+
if (rawEnd != null) diagnoseDerivedEnd(rawEnd, canonical.end, diagnostics);
|
|
152
|
+
return canonical;
|
|
153
|
+
}
|
|
154
|
+
function readTrackValue(rawValue, attribute, source, diagnostics) {
|
|
155
|
+
const trackIndex = parseNumeric(rawValue);
|
|
156
|
+
if (trackIndex == null || !Number.isInteger(trackIndex)) {
|
|
157
|
+
pushDiagnostic(diagnostics, "invalid-track-index", attribute, rawValue);
|
|
158
|
+
return { trackIndex: 0, trackSource: "invalid" };
|
|
159
|
+
}
|
|
160
|
+
return { trackIndex, trackSource: source };
|
|
161
|
+
}
|
|
162
|
+
function readTrack(attributes, diagnostics) {
|
|
163
|
+
const rawTrack = attributes.getAttribute(COMPOSITION_ATTRIBUTES.trackIndex);
|
|
164
|
+
const rawLayer = attributes.getAttribute(COMPOSITION_ATTRIBUTES.legacyTrack);
|
|
165
|
+
if (rawTrack == null && rawLayer == null) return { trackIndex: 0, trackSource: "default" };
|
|
166
|
+
if (rawTrack == null && rawLayer != null) {
|
|
167
|
+
pushDiagnostic(diagnostics, "deprecated-layer", COMPOSITION_ATTRIBUTES.legacyTrack, rawLayer);
|
|
168
|
+
return readTrackValue(
|
|
169
|
+
rawLayer,
|
|
170
|
+
COMPOSITION_ATTRIBUTES.legacyTrack,
|
|
171
|
+
"legacy-layer",
|
|
172
|
+
diagnostics
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
const canonical = readTrackValue(
|
|
176
|
+
rawTrack ?? "",
|
|
177
|
+
COMPOSITION_ATTRIBUTES.trackIndex,
|
|
178
|
+
"track-index",
|
|
179
|
+
diagnostics
|
|
180
|
+
);
|
|
181
|
+
if (rawLayer == null) return canonical;
|
|
182
|
+
pushDiagnostic(diagnostics, "deprecated-layer", COMPOSITION_ATTRIBUTES.legacyTrack, rawLayer);
|
|
183
|
+
const parsedLayer = parseNumeric(rawLayer);
|
|
184
|
+
if (parsedLayer != null && parsedLayer !== canonical.trackIndex) {
|
|
185
|
+
pushDiagnostic(diagnostics, "conflicting-layer", COMPOSITION_ATTRIBUTES.legacyTrack, rawLayer);
|
|
186
|
+
}
|
|
187
|
+
return canonical;
|
|
188
|
+
}
|
|
189
|
+
function readClipTiming(attributes, options = {}) {
|
|
190
|
+
const diagnostics = [];
|
|
191
|
+
const rawStart = attributes.getAttribute(COMPOSITION_ATTRIBUTES.start);
|
|
192
|
+
const startExpression = parseStartExpression(rawStart);
|
|
193
|
+
const start = resolveStart(startExpression, rawStart, options, diagnostics);
|
|
194
|
+
const duration = readDuration(attributes, start, diagnostics);
|
|
195
|
+
const track = readTrack(attributes, diagnostics);
|
|
196
|
+
return { startExpression, start, ...duration, ...track, diagnostics };
|
|
197
|
+
}
|
|
198
|
+
function serializeStartNumber(start) {
|
|
199
|
+
if (!Number.isFinite(start)) {
|
|
200
|
+
throw new ClipTimingWriteError("invalid-start", "start must be finite");
|
|
201
|
+
}
|
|
202
|
+
return String(start);
|
|
203
|
+
}
|
|
204
|
+
function serializeStartReference(start) {
|
|
205
|
+
if (start.kind === "absolute") return serializeStartNumber(start.value);
|
|
206
|
+
if (!start.refId || !Number.isFinite(start.offset)) {
|
|
207
|
+
throw new ClipTimingWriteError(
|
|
208
|
+
"invalid-start",
|
|
209
|
+
"reference start must have an id and finite offset"
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
if (start.offset === 0) return start.refId;
|
|
213
|
+
return `${start.refId} ${start.offset < 0 ? "-" : "+"} ${Math.abs(start.offset)}`;
|
|
214
|
+
}
|
|
215
|
+
function serializeStart(start) {
|
|
216
|
+
if (typeof start === "number") return serializeStartNumber(start);
|
|
217
|
+
if (typeof start === "object" && start != null) return serializeStartReference(start);
|
|
218
|
+
if (typeof start === "string" && parseStartExpression(start)) return start.trim();
|
|
219
|
+
throw new ClipTimingWriteError("invalid-start", `invalid start expression: ${start ?? ""}`);
|
|
220
|
+
}
|
|
221
|
+
function writeDuration(attributes, duration, current) {
|
|
222
|
+
if (duration === null) {
|
|
223
|
+
attributes.removeAttribute(COMPOSITION_ATTRIBUTES.duration);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (duration === void 0) {
|
|
227
|
+
if (attributes.getAttribute(COMPOSITION_ATTRIBUTES.duration) == null && current.duration != null) {
|
|
228
|
+
attributes.setAttribute(COMPOSITION_ATTRIBUTES.duration, String(current.duration));
|
|
229
|
+
}
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if (!Number.isFinite(duration) || duration < 0) {
|
|
233
|
+
throw new ClipTimingWriteError(
|
|
234
|
+
"invalid-duration",
|
|
235
|
+
"duration must be a finite, non-negative number"
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
attributes.setAttribute(COMPOSITION_ATTRIBUTES.duration, String(duration));
|
|
239
|
+
}
|
|
240
|
+
function writeTrack(attributes, trackIndex, current) {
|
|
241
|
+
if (trackIndex === null) {
|
|
242
|
+
attributes.removeAttribute(COMPOSITION_ATTRIBUTES.trackIndex);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
if (trackIndex === void 0) {
|
|
246
|
+
if (attributes.getAttribute(COMPOSITION_ATTRIBUTES.trackIndex) == null && current.trackSource === "legacy-layer") {
|
|
247
|
+
attributes.setAttribute(COMPOSITION_ATTRIBUTES.trackIndex, String(current.trackIndex));
|
|
248
|
+
}
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
if (!Number.isFinite(trackIndex) || !Number.isInteger(trackIndex)) {
|
|
252
|
+
throw new ClipTimingWriteError("invalid-track-index", "trackIndex must be a finite integer");
|
|
253
|
+
}
|
|
254
|
+
attributes.setAttribute(COMPOSITION_ATTRIBUTES.trackIndex, String(trackIndex));
|
|
255
|
+
}
|
|
256
|
+
function writeClipTiming(attributes, update) {
|
|
257
|
+
const current = readClipTiming(attributes);
|
|
258
|
+
if (update.start !== void 0) {
|
|
259
|
+
attributes.setAttribute(COMPOSITION_ATTRIBUTES.start, serializeStart(update.start));
|
|
260
|
+
}
|
|
261
|
+
writeDuration(attributes, update.duration, current);
|
|
262
|
+
writeTrack(attributes, update.trackIndex, current);
|
|
263
|
+
const preserveUnresolvedLegacyEnd = update.duration === void 0 && attributes.getAttribute(COMPOSITION_ATTRIBUTES.duration) == null && attributes.getAttribute(COMPOSITION_ATTRIBUTES.derivedEnd) != null && current.duration == null;
|
|
264
|
+
if (!preserveUnresolvedLegacyEnd) {
|
|
265
|
+
attributes.removeAttribute(COMPOSITION_ATTRIBUTES.derivedEnd);
|
|
266
|
+
}
|
|
267
|
+
attributes.removeAttribute(COMPOSITION_ATTRIBUTES.legacyTrack);
|
|
268
|
+
return readClipTiming(attributes);
|
|
269
|
+
}
|
|
270
|
+
export {
|
|
271
|
+
CANONICAL_AUTHORED_TIMING_ATTRIBUTES,
|
|
272
|
+
COMPOSITION_ATTRIBUTES,
|
|
273
|
+
COMPOSITION_CONTRACT_VERSION,
|
|
274
|
+
ClipTimingWriteError,
|
|
275
|
+
DERIVED_TIMING_ATTRIBUTES,
|
|
276
|
+
LEGACY_TIMING_ATTRIBUTES,
|
|
277
|
+
parseNumeric,
|
|
278
|
+
parseStartExpression,
|
|
279
|
+
readClipTiming,
|
|
280
|
+
writeClipTiming
|
|
281
|
+
};
|
|
282
|
+
//# sourceMappingURL=compositionContract.js.map
|