@hyperframes/parsers 0.8.144 → 0.8.146

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.
Files changed (44) hide show
  1. package/dist/assetResolution.js +0 -1
  2. package/dist/assets.js +0 -1
  3. package/dist/colorGradingContract.js +0 -1
  4. package/dist/composition.js +0 -1
  5. package/dist/compositionContract.js +0 -1
  6. package/dist/compositionDuration.js +0 -1
  7. package/dist/ffBinaries.d.ts +5 -4
  8. package/dist/ffBinaries.js +40 -10
  9. package/dist/gsapConstants.js +0 -1
  10. package/dist/gsapParser.js +0 -1
  11. package/dist/gsapParserAcorn.js +0 -1
  12. package/dist/gsapParserExports.js +0 -1
  13. package/dist/gsapWriterAcorn.js +0 -1
  14. package/dist/hfIds.js +0 -1
  15. package/dist/htmlAttributeSpans.js +0 -1
  16. package/dist/index.js +1 -2
  17. package/dist/mediaDuration.js +0 -1
  18. package/dist/mediaDurationFixtures.js +0 -1
  19. package/dist/slideshow.js +1 -2
  20. package/dist/springEase.js +0 -1
  21. package/dist/subCompositionValidity.js +0 -1
  22. package/dist/topLevelElements.js +0 -1
  23. package/package.json +2 -2
  24. package/dist/assetResolution.js.map +0 -1
  25. package/dist/assets.js.map +0 -1
  26. package/dist/colorGradingContract.js.map +0 -1
  27. package/dist/composition.js.map +0 -1
  28. package/dist/compositionContract.js.map +0 -1
  29. package/dist/compositionDuration.js.map +0 -1
  30. package/dist/ffBinaries.js.map +0 -1
  31. package/dist/gsapConstants.js.map +0 -1
  32. package/dist/gsapParser.js.map +0 -1
  33. package/dist/gsapParserAcorn.js.map +0 -1
  34. package/dist/gsapParserExports.js.map +0 -1
  35. package/dist/gsapWriterAcorn.js.map +0 -1
  36. package/dist/hfIds.js.map +0 -1
  37. package/dist/htmlAttributeSpans.js.map +0 -1
  38. package/dist/index.js.map +0 -1
  39. package/dist/mediaDuration.js.map +0 -1
  40. package/dist/mediaDurationFixtures.js.map +0 -1
  41. package/dist/slideshow.js.map +0 -1
  42. package/dist/springEase.js.map +0 -1
  43. package/dist/subCompositionValidity.js.map +0 -1
  44. package/dist/topLevelElements.js.map +0 -1
@@ -241,4 +241,3 @@ export {
241
241
  resolveLocalAssetCandidates,
242
242
  resolveProjectRelativeSrc
243
243
  };
244
- //# sourceMappingURL=assetResolution.js.map
package/dist/assets.js CHANGED
@@ -118,4 +118,3 @@ export {
118
118
  rewriteInlineStyleAssetUrls,
119
119
  splitUrlSuffix
120
120
  };
121
- //# sourceMappingURL=assets.js.map
@@ -501,4 +501,3 @@ export {
501
501
  isColorGradingVariableRef,
502
502
  validateColorGradingContract
503
503
  };
504
- //# sourceMappingURL=colorGradingContract.js.map
@@ -763,4 +763,3 @@ export {
763
763
  scanVariableUsage,
764
764
  writeClipTiming
765
765
  };
766
- //# sourceMappingURL=composition.js.map
@@ -291,4 +291,3 @@ export {
291
291
  readClipTiming,
292
292
  writeClipTiming
293
293
  };
294
- //# sourceMappingURL=compositionContract.js.map
@@ -21,4 +21,3 @@ export {
21
21
  TIMING_TOLERANCE_SECONDS,
22
22
  resolveCompositionDuration
23
23
  };
24
- //# sourceMappingURL=compositionDuration.js.map
@@ -19,10 +19,11 @@ interface FindFfBinaryOptions {
19
19
  /**
20
20
  * Resolve an FFmpeg-family binary: env override first, then a native
21
21
  * current-directory/PATH scan on Windows or `which` plus PATH scan on Unix,
22
- * then a project-local
23
- * `.hyperframes/bin`, then well-known Unix install dirs. System lookups are
24
- * cached per binary for the process lifetime; the env override is re-read on
25
- * every call.
22
+ * then a project-local `.hyperframes/bin`, then well-known Unix install dirs, then an
23
+ * `@ffmpeg-installer`/`@ffprobe-installer` package reachable from the current folder.
24
+ * A found binary is cached for the process lifetime; a miss only for a few
25
+ * seconds, so a long-running server sees an install made after it started. The env
26
+ * override is re-read on every call.
26
27
  */
27
28
  declare function findFfBinary(name: FfBinaryName, options?: FindFfBinaryOptions): string | undefined;
28
29
  /** Test hook: drop cached system lookups so resolution can be re-exercised. */
@@ -1,7 +1,8 @@
1
1
  // src/ffBinaries.ts
2
2
  import { execFileSync } from "child_process";
3
3
  import { accessSync, constants, existsSync } from "fs";
4
- import { delimiter, join, resolve } from "path";
4
+ import { createRequire } from "module";
5
+ import { delimiter, dirname, join, resolve } from "path";
5
6
  var FFMPEG_PATH_ENV = "HYPERFRAMES_FFMPEG_PATH";
6
7
  var FFPROBE_PATH_ENV = "HYPERFRAMES_FFPROBE_PATH";
7
8
  var ENV_BY_NAME = {
@@ -9,6 +10,8 @@ var ENV_BY_NAME = {
9
10
  ffprobe: FFPROBE_PATH_ENV
10
11
  };
11
12
  var pathLookupCache = /* @__PURE__ */ new Map();
13
+ var MISS_TTL_MS = 5e3;
14
+ var lastMissAt = /* @__PURE__ */ new Map();
12
15
  function candidateFileName(candidate) {
13
16
  return candidate.split(/[\\/]/).at(-1)?.toLowerCase() ?? candidate.toLowerCase();
14
17
  }
@@ -49,20 +52,36 @@ function scanPath(name) {
49
52
  return chooseBestPathCandidate(name, candidates);
50
53
  }
51
54
  var COMMON_BIN_DIRS = process.platform === "win32" ? [] : ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin", "/snap/bin"];
52
- function findInCommonDirs(name) {
55
+ function findInCommonDirsOrInstallerPackage(name) {
53
56
  for (const dir of COMMON_BIN_DIRS) {
54
57
  const candidate = `${dir}/${name}`;
55
58
  if (existsSync(candidate)) return candidate;
56
59
  }
57
- return void 0;
60
+ return findInInstallerPackage(name);
61
+ }
62
+ function binaryFileName(name) {
63
+ return process.platform === "win32" ? `${name}.exe` : name;
58
64
  }
59
65
  function findInProjectLocalBin(name) {
60
- const extension = process.platform === "win32" ? ".exe" : "";
61
- const candidate = resolve(".hyperframes", "bin", `${name}${extension}`);
66
+ const candidate = resolve(".hyperframes", "bin", binaryFileName(name));
62
67
  return existsSync(candidate) ? candidate : void 0;
63
68
  }
64
- function lookupOnSystem(name) {
65
- if (pathLookupCache.has(name)) return pathLookupCache.get(name);
69
+ function tryResolve(fromFile, request) {
70
+ try {
71
+ return createRequire(fromFile).resolve(request);
72
+ } catch {
73
+ return void 0;
74
+ }
75
+ }
76
+ function findInInstallerPackage(name) {
77
+ const project = resolve("package.json");
78
+ const platformManifest = `@${name}-installer/${process.platform}-${process.arch}/package.json`;
79
+ const installer = tryResolve(project, `@${name}-installer/${name}/package.json`);
80
+ const manifest = (installer && tryResolve(installer, platformManifest)) ?? tryResolve(project, platformManifest);
81
+ const candidate = manifest && join(dirname(manifest), binaryFileName(name));
82
+ return candidate && isExecutablePathCandidate(candidate) ? candidate : void 0;
83
+ }
84
+ function searchSystem(name) {
66
85
  let found;
67
86
  if (process.platform === "win32") {
68
87
  found = scanPath(name);
@@ -80,8 +99,19 @@ function lookupOnSystem(name) {
80
99
  }
81
100
  }
82
101
  found ??= findInProjectLocalBin(name);
83
- found ??= findInCommonDirs(name);
84
- const resolved = found ? resolve(found) : void 0;
102
+ found ??= findInCommonDirsOrInstallerPackage(name);
103
+ return found;
104
+ }
105
+ function lookupOnSystem(name) {
106
+ const cached = pathLookupCache.get(name);
107
+ if (cached) return cached;
108
+ if (Date.now() - (lastMissAt.get(name) ?? -Infinity) < MISS_TTL_MS) return void 0;
109
+ const found = searchSystem(name);
110
+ if (!found) {
111
+ lastMissAt.set(name, Date.now());
112
+ return void 0;
113
+ }
114
+ const resolved = resolve(found);
85
115
  pathLookupCache.set(name, resolved);
86
116
  return resolved;
87
117
  }
@@ -95,6 +125,7 @@ function findFfBinary(name, options = {}) {
95
125
  }
96
126
  function clearFfBinaryLookupCache() {
97
127
  pathLookupCache.clear();
128
+ lastMissAt.clear();
98
129
  }
99
130
  export {
100
131
  FFMPEG_PATH_ENV,
@@ -102,4 +133,3 @@ export {
102
133
  clearFfBinaryLookupCache,
103
134
  findFfBinary
104
135
  };
105
- //# sourceMappingURL=ffBinaries.js.map
@@ -185,4 +185,3 @@ export {
185
185
  isXYPositionWrite,
186
186
  keyframeHoldForAnimation
187
187
  };
188
- //# sourceMappingURL=gsapConstants.js.map
@@ -2881,4 +2881,3 @@ export {
2881
2881
  updateMotionPathPointInScript,
2882
2882
  validateCompositionGsap
2883
2883
  };
2884
- //# sourceMappingURL=gsapParser.js.map
@@ -2013,4 +2013,3 @@ export {
2013
2013
  parseGsapScriptAcornForWrite,
2014
2014
  scriptShape
2015
2015
  };
2016
- //# sourceMappingURL=gsapParserAcorn.js.map
@@ -2177,4 +2177,3 @@ export {
2177
2177
  serializeGsapAnimations,
2178
2178
  validateCompositionGsap
2179
2179
  };
2180
- //# sourceMappingURL=gsapParserExports.js.map
@@ -3346,4 +3346,3 @@ export {
3346
3346
  updateKeyframeInScript,
3347
3347
  updateMotionPathPointInScript
3348
3348
  };
3349
- //# sourceMappingURL=gsapWriterAcorn.js.map
package/dist/hfIds.js CHANGED
@@ -111,4 +111,3 @@ export {
111
111
  mintHfId,
112
112
  walkCompositionDescendants
113
113
  };
114
- //# sourceMappingURL=hfIds.js.map
@@ -97,4 +97,3 @@ export {
97
97
  decodeAuthoredAttribute,
98
98
  scanHtmlOpeningTags
99
99
  };
100
- //# sourceMappingURL=htmlAttributeSpans.js.map
package/dist/index.js CHANGED
@@ -3485,7 +3485,7 @@ function buildAspectMismatch(compositionWidth, compositionHeight, outputResoluti
3485
3485
  compositionHeight,
3486
3486
  outputResolution
3487
3487
  );
3488
- const suggestion = suggestedResolution ? ` The composition is ${describeOrientation(compositionWidth, compositionHeight)} \u2014 use --resolution ${suggestedResolution} instead.` : ` Pick a preset whose orientation matches, or omit --resolution to render at the composition's native dimensions.`;
3488
+ const suggestion = suggestedResolution ? ` The composition is ${describeOrientation(compositionWidth, compositionHeight)}, so use --resolution ${suggestedResolution} instead.` : ` Pick a preset whose orientation matches, or omit --resolution to render at the composition's native dimensions.`;
3489
3489
  return {
3490
3490
  ok: false,
3491
3491
  kind: "aspect-mismatch",
@@ -4462,4 +4462,3 @@ export {
4462
4462
  walkCompositionDescendants,
4463
4463
  writeClipTiming
4464
4464
  };
4465
- //# sourceMappingURL=index.js.map
@@ -101,4 +101,3 @@ export {
101
101
  resolveMediaDuration,
102
102
  resolveNaturalDurationSeconds
103
103
  };
104
- //# sourceMappingURL=mediaDuration.js.map
@@ -81,4 +81,3 @@ var MEDIA_DURATION_FIXTURES = [
81
81
  export {
82
82
  MEDIA_DURATION_FIXTURES
83
83
  };
84
- //# sourceMappingURL=mediaDurationFixtures.js.map
package/dist/slideshow.js CHANGED
@@ -111,7 +111,7 @@ function resolveSlideshow(manifest, scenes) {
111
111
  const sequences = {};
112
112
  for (const seq of manifest.slideSequences ?? []) {
113
113
  if (Object.prototype.hasOwnProperty.call(sequences, seq.id)) {
114
- errors.push(`duplicate slideSequence id "${seq.id}" \u2014 only the last definition is kept`);
114
+ errors.push(`duplicate slideSequence id "${seq.id}": only the last definition is kept`);
115
115
  }
116
116
  sequences[seq.id] = {
117
117
  id: seq.id,
@@ -158,4 +158,3 @@ export {
158
158
  resolveSlideshow,
159
159
  slideshowIslandRegex
160
160
  };
161
- //# sourceMappingURL=slideshow.js.map
@@ -41,4 +41,3 @@ export {
41
41
  SPRING_PRESETS,
42
42
  generateSpringEaseData
43
43
  };
44
- //# sourceMappingURL=springEase.js.map
@@ -52,4 +52,3 @@ export {
52
52
  checkSubCompositionUsability,
53
53
  resolveSubCompositionContent
54
54
  };
55
- //# sourceMappingURL=subCompositionValidity.js.map
@@ -37,4 +37,3 @@ export {
37
37
  topLevelElements,
38
38
  trackKindOf
39
39
  };
40
- //# sourceMappingURL=topLevelElements.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/parsers",
3
- "version": "0.8.144",
3
+ "version": "0.8.146",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/heygen-com/hyperframes",
@@ -119,7 +119,7 @@
119
119
  "tsx": "^4.21.0",
120
120
  "typescript": "^5.0.0",
121
121
  "vitest": "^4.1.11",
122
- "@hyperframes/core": "0.8.144"
122
+ "@hyperframes/core": "0.8.146"
123
123
  },
124
124
  "scripts": {
125
125
  "build": "tsup",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/htmlAttributeSpans.ts","../src/assetResolution.ts","../src/utils/urlPath.ts"],"sourcesContent":["import { decodeHTMLAttribute } from \"entities/decode\";\n\nexport type HtmlAttributeSpan = {\n name: string;\n start: number;\n end: number;\n} & (\n | { kind: \"boolean\" }\n | { kind: \"value\"; value: string; valueStart: number; valueEnd: number; quote: string }\n);\n\nexport interface HtmlOpeningTagSpan {\n name: string;\n start: number;\n end: number;\n bodyEnd: number;\n closed: boolean;\n rawTextEnd: number | null;\n attributes: HtmlAttributeSpan[];\n}\n\nfunction readAttributeValue(html: string, at: number) {\n while (/[\\t\\n\\f\\r ]/.test(html.charAt(at))) at++;\n const quote = ['\"', \"'\"].includes(html.charAt(at)) ? html.charAt(at) : \"\";\n if (quote) at++;\n const valueStart = at;\n if (quote) {\n const closing = html.indexOf(quote, at);\n at = closing === -1 ? html.length : closing;\n } else {\n while (at < html.length && !/[\\t\\n\\f\\r >]/.test(html[at]!)) at++;\n }\n const valueEnd = at;\n const end = quote ? Math.min(valueEnd + 1, html.length) : valueEnd;\n return { value: html.slice(valueStart, valueEnd), valueStart, valueEnd, quote, end };\n}\n\nfunction readAttribute(html: string, start: number): HtmlAttributeSpan | null {\n let at = start;\n while (at < html.length && !/[\\t\\n\\f\\r =/>]/.test(html[at]!)) at++;\n if (at === start) return null;\n const name = html.slice(start, at).toLowerCase();\n const nameEnd = at;\n while (/[\\t\\n\\f\\r ]/.test(html[at] ?? \"\")) at++;\n if (html[at] !== \"=\") return { kind: \"boolean\", name, start, end: nameEnd };\n return { kind: \"value\", name, start, ...readAttributeValue(html, at + 1) };\n}\n\nfunction readOpeningTag(html: string, start: number, name: string, at: number): HtmlOpeningTagSpan {\n const attributes: HtmlAttributeSpan[] = [];\n while (at < html.length) {\n while (/[\\t\\n\\f\\r /]/.test(html[at] ?? \"\")) at++;\n if (at >= html.length || html[at] === \">\") break;\n const attribute = readAttribute(html, at);\n if (attribute === null) {\n at++;\n continue;\n }\n attributes.push(attribute);\n at = attribute.end;\n }\n const closed = html[at] === \">\";\n return {\n name: name.toLowerCase(),\n start,\n bodyEnd: at,\n end: closed ? at + 1 : at,\n closed,\n rawTextEnd: null,\n attributes,\n };\n}\n\nexport function scanHtmlOpeningTags(html: string): HtmlOpeningTagSpan[] {\n const tags: HtmlOpeningTagSpan[] = [];\n const opening = /<!--[\\s\\S]*?(?:--!?>|$)|<([a-z][^\\t\\n\\f\\r />]*)/gi;\n let match: RegExpExecArray | null;\n while ((match = opening.exec(html)) !== null) {\n if (!match[1]) continue;\n const tag = readOpeningTag(html, match.index, match[1], opening.lastIndex);\n tags.push(tag);\n opening.lastIndex = tag.end;\n if (\n [\n \"script\",\n \"style\",\n \"textarea\",\n \"title\",\n \"xmp\",\n \"iframe\",\n \"noembed\",\n \"noframes\",\n \"plaintext\",\n ].includes(tag.name)\n ) {\n if (tag.name === \"plaintext\") {\n tag.rawTextEnd = html.length;\n break;\n }\n const closing = new RegExp(`</${tag.name}(?=[\\\\t\\\\n\\\\f\\\\r />])`, \"gi\");\n closing.lastIndex = tag.end;\n const end = closing.exec(html);\n tag.rawTextEnd = end?.index ?? html.length;\n opening.lastIndex = tag.rawTextEnd;\n }\n }\n return tags;\n}\n\nexport const HTML_ATTRIBUTE_ENTITIES: Record<string, readonly string[]> = {\n \"&\": [\"&amp;\"],\n '\"': [\"&quot;\"],\n \"'\": [\"&#39;\", \"&apos;\"],\n \"<\": [\"&lt;\"],\n \">\": [\"&gt;\"],\n};\nexport function decodeAuthoredAttribute(value: string): string {\n return decodeHTMLAttribute(value);\n}\n","import { scanHtmlOpeningTags, decodeAuthoredAttribute } from \"./htmlAttributeSpans.js\";\nimport { closeSync, constants, existsSync, fstatSync, openSync, readFileSync } from \"node:fs\";\nimport { isAbsolute, join, posix, relative, resolve, sep } from \"node:path\";\nimport { decodeUrlPathVariants } from \"./composition.js\";\n\n/** The subset of `node:path` that `isWithinProjectRoot` needs to run under\n * an injected platform (tests pass `path.win32` / `path.posix`). */\ninterface PathModuleLike {\n resolve: (...segments: string[]) => string;\n relative: (from: string, to: string) => string;\n isAbsolute: (path: string) => boolean;\n}\n\n/**\n * Shared local-asset resolution helpers for every package that maps\n * composition asset URLs to files on disk (lint project rules, the HEVC\n * preview check, studio-server's media codec scan). Import via the\n * `@hyperframes/parsers/asset-resolution` subpath.\n */\n\nexport function isRemoteOrInlineUrl(url: string): boolean {\n return /^(https?:|data:|blob:|\\/\\/|#)/i.test(url);\n}\n\n/**\n * True when a URL still contains an unresolved templating placeholder —\n * `<<token>>`, `{{ token }}`, or `${token}` — that a build or templating step\n * substitutes before render. The static linter runs before that substitution,\n * so it cannot resolve such a value to a file on disk and must not report it as\n * a missing asset. Check the RAW url, before any `cleanAssetUrl()` step: that\n * splits on `?`/`#`, which also chops inside a `${...}` expression. (The `__UPPER__`\n * placeholder shape is combined with this in `isUnresolvedAssetPlaceholder` below, the\n * shared predicate asset-src sites skip on.)\n */\nexport function hasUnresolvedTemplatingToken(url: string): boolean {\n return /<<[^<>]+>>|\\{\\{[^{}]+\\}\\}|\\$\\{[^{}]+\\}/.test(url);\n}\n\n/**\n * True when an asset src is a build-time placeholder rather than a resolvable path:\n * the `__UPPER__` shape (e.g. `__DURATION__`) or an unresolved templating token\n * (`<<...>>`, `{{...}}`, `${...}`). Pass the RAW src, before any `cleanAssetUrl()` step —\n * cleanAssetUrl splits on `?`/`#`, which would chop inside a `${...}` expression and defeat\n * the token match. Remote/inline URL handling is deliberately NOT folded in: call sites\n * differ (e.g. audio uses a narrower http/data/blob check), so each keeps its own.\n *\n * This is the single skip predicate every asset-src lint / codec / compile site should\n * route through, so the placeholder rules can't drift apart across call sites.\n */\nexport function isUnresolvedAssetPlaceholder(rawSrc: string): boolean {\n return /^__[A-Z_]+__$/.test(rawSrc.trim()) || hasUnresolvedTemplatingToken(rawSrc);\n}\n\n/**\n * Collect active, local composition paths in document order, including template content.\n * Paths are physical file names relative to the project root at every nesting level.\n */\nexport function collectSubCompositionSrcs(html: string): string[] {\n const srcs: string[] = [];\n const seen = new Set<string>();\n\n for (const tag of scanHtmlOpeningTags(html)) {\n if (!tag.closed) continue;\n const attribute = tag.attributes.find((attr) => attr.name === \"data-composition-src\");\n if (attribute?.kind !== \"value\") continue;\n const src = decodeAuthoredAttribute(attribute.value).trim();\n if (!src || seen.has(src)) continue;\n // __UPPER__ placeholder or late-bound templating token — not a real reference.\n if (isUnresolvedAssetPlaceholder(src)) continue;\n // A remote or inline mount names no file on disk. Every caller resolves\n // these against the project root, so letting one through produces a\n // nonsense path (`<projectDir>/https:/host/a.html`) that then reads as a\n // missing local file: a false \"does not exist\" for lint, and a wasted\n // visit against the telemetry walk's file budget.\n if (isRemoteOrInlineUrl(src)) continue;\n seen.add(src);\n srcs.push(src);\n }\n return srcs;\n}\n\nexport function cleanAssetUrl(url: string): string {\n return url.trim().split(/[?#]/, 1)[0] ?? \"\";\n}\n\n/**\n * `pathModule` defaults to the host's native `node:path`, so every existing\n * caller gets its actual OS's separator and drive-letter rules unchanged.\n * Tests inject `path.win32` / `path.posix` to exercise both platforms' rules\n * from a single OS (same pattern as producer/fileServer.ts's `isPathInside`).\n */\nexport function isWithinProjectRoot(\n projectDir: string,\n candidate: string,\n pathModule: PathModuleLike = { resolve, relative, isAbsolute },\n): boolean {\n const projectRoot = pathModule.resolve(projectDir);\n const relativePath = pathModule.relative(projectRoot, candidate);\n return (\n relativePath === \"\" || (!relativePath.startsWith(\"..\") && !pathModule.isAbsolute(relativePath))\n );\n}\n\nfunction addCandidate(candidates: string[], candidate: string): void {\n if (!candidates.includes(candidate)) candidates.push(candidate);\n}\n\nexport function resolveLocalAssetCandidates(projectDir: string, url: string): string[] {\n const cleanUrl = cleanAssetUrl(url);\n const projectRoot = resolve(projectDir);\n const candidates: string[] = [];\n\n for (const variant of decodeUrlPathVariants(cleanUrl)) {\n const projectRelative = variant.startsWith(\"/\") ? variant.slice(1) : variant;\n const resolved = resolve(projectRoot, projectRelative);\n if (isWithinProjectRoot(projectRoot, resolved)) {\n addCandidate(candidates, resolved);\n continue;\n }\n\n const normalized = posix.normalize(projectRelative.replace(/\\\\/g, \"/\"));\n const clamped = normalized.replace(/^(\\.\\.\\/)+/, \"\");\n if (clamped && !clamped.startsWith(\"..\")) {\n addCandidate(candidates, resolve(projectRoot, clamped));\n }\n }\n\n return candidates;\n}\n\nexport function resolveExistingLocalAsset(\n projectDir: string,\n url: string,\n): { resolved: string; rootRelativePath: string } | null {\n const projectRoot = resolve(projectDir);\n const resolved = resolveLocalAssetCandidates(projectRoot, url).find(existsSync);\n if (!resolved) return null;\n return { resolved, rootRelativePath: relative(projectRoot, resolved) };\n}\n\n// Candidates for a variant whose join escaped the project root, re-anchored at the root.\nfunction reanchoredCandidates(variant: string, baseDir: string, compiledDir?: string): string[] {\n const baseAbs = resolve(baseDir);\n const joinedAbs = resolve(join(baseDir, variant));\n if (joinedAbs === baseAbs || joinedAbs.startsWith(baseAbs + sep)) return [];\n // Normalize before stripping, or `assets/../../assets/foo` becomes `assets/assets/foo`.\n const stripped = posix.normalize(variant.replace(/\\\\/g, \"/\")).replace(/^(\\.\\.\\/)+/, \"\");\n if (!stripped || stripped === variant || stripped.startsWith(\"..\")) return [];\n return compiledDir\n ? [join(compiledDir, stripped), join(baseDir, stripped)]\n : [join(baseDir, stripped)];\n}\n\n/** Resolves a media `src` like a browser URL (`..` clamps at the project root); a miss returns the base-dir join. */\nexport function resolveProjectRelativeSrc(\n src: string,\n baseDir: string,\n compiledDir?: string,\n): string {\n const cleanSrc = cleanAssetUrl(src);\n\n // A leading slash is an origin-root URL served from the project root, unless the absolute path exists.\n if (isAbsolute(cleanSrc) && existsSync(cleanSrc)) return cleanSrc;\n\n const candidates = new Set<string>();\n for (const variant of decodeUrlPathVariants(cleanSrc)) {\n for (const candidate of reanchoredCandidates(variant, baseDir, compiledDir)) {\n candidates.add(candidate);\n }\n if (compiledDir) candidates.add(join(compiledDir, variant));\n candidates.add(join(baseDir, variant));\n }\n return [...candidates].find(existsSync) ?? join(baseDir, cleanSrc);\n}\n\nfunction maskRange(src: string, pattern: RegExp): string {\n return src.replace(pattern, (m) => \" \".repeat(m.length));\n}\n\nfunction maskHtmlComments(src: string): string {\n const chunks: string[] = [];\n let cursor = 0;\n\n while (true) {\n const start = src.indexOf(\"<!--\", cursor);\n if (start === -1) break;\n const end = src.indexOf(\"-->\", start + 4);\n if (end === -1) break;\n const afterComment = end + 3;\n chunks.push(src.slice(cursor, start), \" \".repeat(afterComment - start));\n cursor = afterComment;\n }\n\n return chunks.length === 0 ? src : chunks.join(\"\") + src.slice(cursor);\n}\n\n/** Blanks out comments, `<style>`, and `<script>` bodies so tag-scanning\n * regexes don't false-positive on commented-out or scripted markup. */\nexport function maskNonScannableRanges(html: string): string {\n let out = maskHtmlComments(html);\n out = maskRange(out, /<style\\b[^>]*>[\\s\\S]*?<\\/style\\b[^>]*>/gi);\n out = maskRange(out, /<script\\b[^>]*>[\\s\\S]*?<\\/script\\b[^>]*>/gi);\n return out;\n}\n\nexport type ProjectFileRead =\n | { kind: \"file\"; text: string }\n | { kind: \"folder\" }\n | { kind: \"missing\" };\n\n/** Reads through one descriptor, so the file-type check and the read see the same file. */\nexport function readProjectFile(path: string): ProjectFileRead {\n let fd: number;\n try {\n // Non-blocking so a named pipe is reported, not waited on; the mode never applies (no O_CREAT).\n fd = openSync(path, constants.O_RDONLY | (constants.O_NONBLOCK ?? 0), 0o600);\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code ?? \"\";\n if ([\"EISDIR\", \"ENXIO\"].includes(code)) return { kind: \"folder\" };\n if ([\"ENOENT\", \"ENOTDIR\", \"ELOOP\", \"ENAMETOOLONG\"].includes(code)) return { kind: \"missing\" };\n throw error;\n }\n try {\n if (!fstatSync(fd).isFile()) return { kind: \"folder\" };\n return { kind: \"file\", text: readFileSync(fd, \"utf-8\") };\n } finally {\n closeSync(fd);\n }\n}\n","export function decodeUrlPathVariants(path: string): string[] {\n const decoded = decodeWellFormedEscapes(path);\n return decoded === path ? [path] : [decoded, path];\n}\n\n// A browser sends src=\"100%.png\" as-is, so a % without two hex digits after it is the file's own.\nexport function decodeWellFormedEscapes(path: string): string {\n return path.replace(/(?:%[0-9A-Fa-f]{2})+/g, (run) => {\n try {\n return decodeURIComponent(run);\n } catch {\n return run;\n }\n });\n}\n\nexport function splitUrlSuffix(urlValue: string): { basePath: string; suffix: string } {\n const queryIdx = urlValue.indexOf(\"?\");\n const hashIdx = urlValue.indexOf(\"#\");\n if (queryIdx < 0 && hashIdx < 0) return { basePath: urlValue, suffix: \"\" };\n let cutIdx = urlValue.length;\n if (queryIdx >= 0) cutIdx = Math.min(cutIdx, queryIdx);\n if (hashIdx >= 0) cutIdx = Math.min(cutIdx, hashIdx);\n return { basePath: urlValue.slice(0, cutIdx), suffix: urlValue.slice(cutIdx) };\n}\n\nexport function decodedUrlPath(url: string): string {\n return decodeWellFormedEscapes(splitUrlSuffix(url).basePath);\n}\n\nexport function encodeUrlPath(path: string): string {\n return encodeURIComponent(path)\n .replace(/%2F/g, \"/\")\n .replace(/['()]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`);\n}\n\nexport function decodeCssEscapes(text: string): string {\n return text\n .replaceAll(\"\\0\", \"\\uFFFD\")\n .replace(\n /\\\\(?:([0-9a-f]{1,6})(?:\\r\\n|[ \\t\\r\\n\\f])?|(\\r\\n|[\\n\\r\\f])|([\\s\\S]))/gi,\n (\n _escaped,\n hex: string | undefined,\n lineBreak: string | undefined,\n char: string | undefined,\n ) => {\n if (hex === undefined) return lineBreak === undefined ? char! : \"\";\n const code = Number.parseInt(hex, 16);\n if (code === 0 || code > 0x10ffff || (code >= 0xd800 && code <= 0xdfff)) return \"\\uFFFD\";\n return String.fromCodePoint(code);\n },\n );\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/assetPaths.ts","../src/utils/urlPath.ts","../src/rewriteSubCompPaths.ts"],"sourcesContent":["/**\n * Shared primitives for scanning and rewriting asset paths in HTML/CSS.\n *\n * Used by: rewriteSubCompPaths (core), collectExternalAssets (producer),\n * localizeExternalAssets (CLI publish).\n */\n\nimport { isAbsolute, relative, resolve } from \"node:path\";\n\n/**\n * Regex matching CSS `url(...)` references — captures the quote style and the\n * raw URL. The URL group is anchored to non-whitespace at both ends so the\n * surrounding `\\s*` can never overlap it (avoids polynomial-ReDoS backtracking);\n * the captured value is whitespace-bounded already, matching the old behavior\n * after callers `.trim()` it.\n */\nexport const CSS_URL_RE = /\\burl\\(\\s*([\"']?)([^)\"'\\s](?:[^)\"']*[^)\"'\\s])?)\\1\\s*\\)/g;\n\n/** Attributes that may contain relative asset paths. */\nexport const PATH_ATTRS = [\"src\", \"href\"] as const;\n\n/** Returns true for URLs/prefixes that should never be rewritten. */\nexport function isNonRelativeUrl(val: string): boolean {\n return (\n !val ||\n val.startsWith(\"http://\") ||\n val.startsWith(\"https://\") ||\n val.startsWith(\"//\") ||\n val.startsWith(\"data:\") ||\n val.startsWith(\"#\") ||\n val.startsWith(\"/\")\n );\n}\n\n/**\n * Cross-platform containment check: is `childPath` inside `parentPath`?\n * Equality counts as \"inside\".\n */\nexport function isPathInside(childPath: string, parentPath: string): boolean {\n const absChild = resolve(childPath);\n const absParent = resolve(parentPath);\n if (absChild === absParent) return true;\n const rel = relative(absParent, absChild);\n return rel !== \"\" && !rel.startsWith(\"..\") && !isAbsolute(rel);\n}\n\nexport {\n decodeCssEscapes,\n decodeWellFormedEscapes,\n decodedUrlPath,\n splitUrlSuffix,\n encodeUrlPath,\n} from \"./utils/urlPath.js\";\n","export function decodeUrlPathVariants(path: string): string[] {\n const decoded = decodeWellFormedEscapes(path);\n return decoded === path ? [path] : [decoded, path];\n}\n\n// A browser sends src=\"100%.png\" as-is, so a % without two hex digits after it is the file's own.\nexport function decodeWellFormedEscapes(path: string): string {\n return path.replace(/(?:%[0-9A-Fa-f]{2})+/g, (run) => {\n try {\n return decodeURIComponent(run);\n } catch {\n return run;\n }\n });\n}\n\nexport function splitUrlSuffix(urlValue: string): { basePath: string; suffix: string } {\n const queryIdx = urlValue.indexOf(\"?\");\n const hashIdx = urlValue.indexOf(\"#\");\n if (queryIdx < 0 && hashIdx < 0) return { basePath: urlValue, suffix: \"\" };\n let cutIdx = urlValue.length;\n if (queryIdx >= 0) cutIdx = Math.min(cutIdx, queryIdx);\n if (hashIdx >= 0) cutIdx = Math.min(cutIdx, hashIdx);\n return { basePath: urlValue.slice(0, cutIdx), suffix: urlValue.slice(cutIdx) };\n}\n\nexport function decodedUrlPath(url: string): string {\n return decodeWellFormedEscapes(splitUrlSuffix(url).basePath);\n}\n\nexport function encodeUrlPath(path: string): string {\n return encodeURIComponent(path)\n .replace(/%2F/g, \"/\")\n .replace(/['()]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`);\n}\n\nexport function decodeCssEscapes(text: string): string {\n return text\n .replaceAll(\"\\0\", \"\\uFFFD\")\n .replace(\n /\\\\(?:([0-9a-f]{1,6})(?:\\r\\n|[ \\t\\r\\n\\f])?|(\\r\\n|[\\n\\r\\f])|([\\s\\S]))/gi,\n (\n _escaped,\n hex: string | undefined,\n lineBreak: string | undefined,\n char: string | undefined,\n ) => {\n if (hex === undefined) return lineBreak === undefined ? char! : \"\";\n const code = Number.parseInt(hex, 16);\n if (code === 0 || code > 0x10ffff || (code >= 0xd800 && code <= 0xdfff)) return \"\\uFFFD\";\n return String.fromCodePoint(code);\n },\n );\n}\n","/**\n * Rewrite relative asset paths in sub-composition content so they resolve\n * correctly after the content is inlined into the root document.\n *\n * A sub-composition at \"compositions/scene.html\" referencing \"../icon.svg\"\n * means the project root — but after inlining into root index.html, the\n * \"../\" escapes the project directory and causes 404s. This function\n * resolves each relative path against the sub-composition's directory,\n * then normalizes it to be relative to the project root.\n *\n * Used by both the core bundler (preview) and the producer compiler (render)\n * to ensure consistent behavior.\n */\n\n// URL paths in HTML output are POSIX regardless of host OS — use the `posix`\n// submodule so Windows builds don't emit backslash-separated paths (or worse,\n// drive-letter-prefixed artifacts from `resolve(\"/\", ...)`).\nimport { posix } from \"path\";\nconst { join, resolve, dirname } = posix;\n\nimport {\n CSS_URL_RE,\n PATH_ATTRS,\n isNonRelativeUrl,\n splitUrlSuffix,\n decodeWellFormedEscapes,\n encodeUrlPath,\n} from \"./assetPaths.js\";\n\nconst isAbsoluteOrSpecial = isNonRelativeUrl;\n\n/**\n * Returns true only for paths that traverse up with `../`.\n * Plain relative paths like `assets/foo.svg` are ambiguous: two conventions are\n * in the wild, and only the filesystem can tell them apart — see `AssetExists`.\n */\nfunction needsRewrite(val: string): boolean {\n return val.startsWith(\"../\") || val === \"..\";\n}\n\n/**\n * Probe for \"does this project-root-relative path exist?\", supplied by callers\n * that can see the filesystem (the studio preview builder, the bundler, the\n * producer). Keeps this module free of `node:fs` so it stays browser-safe.\n *\n * It disambiguates the two conventions for a plain relative path authored in a\n * sub-composition that lives in a subdirectory:\n *\n * 1. A SIBLING file — `<link href=\"_shared.css\">` next to the composition.\n * This is what a browser resolves when the file is opened directly, and\n * what an author means. Once the content moves into the project-root\n * document it must become `design/styleframes/_shared.css` or it 404s.\n * 2. A PROJECT-ROOT asset — registry blocks are installed into a\n * subdirectory but reference `assets/logo.png` at the root. Already\n * correct in the root document; rewriting would break it.\n *\n * A sibling that exists on disk means (1); anything else is left as authored.\n * Without a probe the behavior is unchanged — plain relative paths pass through.\n */\nexport type AssetExists = (projectRelativePath: string) => boolean;\n\n/**\n * Rewrite a single relative path from a sub-composition's context to the\n * project root context.\n *\n * @param compSrcPath - The `data-composition-src` value (e.g. \"compositions/scene.html\")\n * @param relativePath - The asset path to rewrite (e.g. \"../icon.svg\")\n * @param assetExists - Optional filesystem probe; see `AssetExists`.\n * @returns The rewritten path relative to project root (e.g. \"icon.svg\"), or\n * the original path if no rewriting is needed.\n */\nexport function rewriteAssetPath(\n compSrcPath: string,\n relativePath: string,\n assetExists?: AssetExists,\n): string {\n if (isAbsoluteOrSpecial(relativePath)) return relativePath;\n const compDir = dirname(compSrcPath);\n if (!compDir || compDir === \".\") return relativePath;\n const { basePath, suffix } = splitUrlSuffix(relativePath);\n const slashed = basePath.replace(/\\\\/g, \"/\");\n if (!slashed || slashed.startsWith(\"/\")) return relativePath;\n const filePath = decodeWellFormedEscapes(slashed);\n const sibling = resolve(\"/\", join(compDir, filePath)).slice(1);\n if (!needsRewrite(filePath) && (!assetExists || !assetExists(sibling))) return relativePath;\n return encodeUrlPath(sibling) + suffix;\n}\n\n/**\n * Rewrite all relative `src` and `href` attributes on elements within a\n * DOM tree, adjusting paths from the sub-composition's directory context\n * to the project root.\n *\n * @param elements - Iterable of DOM elements to scan (e.g. from querySelectorAll)\n * @param compSrcPath - The `data-composition-src` value\n * @param getAttr - Function to read an attribute from an element\n * @param setAttr - Function to set an attribute on an element\n */\nexport function rewriteAssetPaths<T>(\n elements: Iterable<T>,\n compSrcPath: string,\n getAttr: (el: T, attr: string) => string | null | undefined,\n setAttr: (el: T, attr: string, value: string) => void,\n assetExists?: AssetExists,\n): void {\n for (const el of elements) {\n for (const attr of PATH_ATTRS) {\n const val = (getAttr(el, attr) || \"\").trim();\n const rewritten = rewriteAssetPath(compSrcPath, val, assetExists);\n if (rewritten !== val) {\n setAttr(el, attr, rewritten);\n }\n }\n }\n}\n\n/**\n * Rewrite CSS url(...) references inside inline style attributes.\n */\nexport function rewriteInlineStyleAssetUrls<T>(\n elements: Iterable<T>,\n compSrcPath: string,\n getStyle: (el: T) => string | null | undefined,\n setStyle: (el: T, value: string) => void,\n assetExists?: AssetExists,\n): void {\n const compDir = dirname(compSrcPath);\n if (!compDir || compDir === \".\") return;\n\n for (const el of elements) {\n const style = getStyle(el);\n if (!style) continue;\n const rewritten = rewriteCssAssetUrls(style, compSrcPath, assetExists);\n if (rewritten !== style) {\n setStyle(el, rewritten);\n }\n }\n}\n\n/**\n * Rewrite CSS url(...) references in a sub-composition's inline styles so\n * ../foo.woff2 remains valid after the CSS is hoisted into the root document.\n */\nexport function rewriteCssAssetUrls(\n cssText: string,\n compSrcPath: string,\n assetExists?: AssetExists,\n): string {\n if (!cssText) return cssText;\n return cssText.replace(CSS_URL_RE, (full, quote: string, rawUrl: string) => {\n const urlValue = (rawUrl || \"\").trim();\n const rewritten = rewriteAssetPath(compSrcPath, urlValue, assetExists);\n if (rewritten === urlValue) return full;\n return `url(${quote || \"\"}${rewritten}${quote || \"\"})`;\n });\n}\n"],"mappings":"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","names":["resolve"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/colorGradingContract.ts"],"sourcesContent":["export const COLOR_GRADING_CONTRACT_VERSION = 2;\nexport const COLOR_GRADING_COLOR_SPACE = \"rec709\";\nexport const COLOR_GRADING_MAX_CURVE_POINTS = 16;\nexport const COLOR_GRADING_MAX_SECONDARIES = 4;\nexport const COLOR_GRADING_ADVANCED_LIMITS = {\n hueDegrees: { min: 0, max: 360, inclusiveMax: false },\n unit: { min: 0, max: 1 },\n signedUnit: { min: -1, max: 1 },\n secondaryHueRange: { min: 0, max: 180 },\n secondaryHueSoftness: { min: 0, max: 180 },\n secondaryHueCombinedMax: 180,\n secondarySoftRangeSoftness: { min: 0, max: 0.5 },\n secondaryHueShift: { min: -180, max: 180 },\n effects: {\n asciiStyle: { min: 0, max: 7 },\n bloom: { min: 0, max: 3 },\n bloomRadius: { min: 1, max: 100 },\n monoScreenShape: { min: 0, max: 4 },\n },\n} as const;\n\nexport const COLOR_GRADING_TOP_LEVEL_KEYS = [\n \"enabled\",\n \"preset\",\n \"intensity\",\n \"adjust\",\n \"wheels\",\n \"curves\",\n \"hueCurves\",\n \"secondaries\",\n \"details\",\n \"effects\",\n \"palette\",\n \"lut\",\n \"colorSpace\",\n] as const;\n\nexport const COLOR_GRADING_ADJUST_KEYS = [\n \"exposure\",\n \"contrast\",\n \"highlights\",\n \"shadows\",\n \"whites\",\n \"blacks\",\n \"temperature\",\n \"tint\",\n \"vibrance\",\n \"saturation\",\n] as const;\n\nexport const COLOR_GRADING_WHEEL_KEYS = [\"shadows\", \"midtones\", \"highlights\"] as const;\nexport const COLOR_GRADING_CURVE_KEYS = [\"master\", \"red\", \"green\", \"blue\"] as const;\nexport const COLOR_GRADING_HUE_CURVE_KEYS = [\"hueVsHue\", \"hueVsSaturation\", \"hueVsLuma\"] as const;\n\nconst COLOR_GRADING_WHEEL_CONTROL_KEYS = [\"hue\", \"amount\", \"level\"] as const;\nconst COLOR_GRADING_SECONDARY_KEYS = [\"enabled\", \"key\", \"correction\"] as const;\nconst COLOR_GRADING_SECONDARY_KEY_KEYS = [\"hue\", \"saturation\", \"luma\"] as const;\nconst COLOR_GRADING_HUE_RANGE_KEYS = [\"center\", \"range\", \"softness\"] as const;\nconst COLOR_GRADING_SOFT_RANGE_KEYS = [\"min\", \"max\", \"softness\"] as const;\nconst COLOR_GRADING_SECONDARY_CORRECTION_KEYS = [\n \"hueShift\",\n \"saturation\",\n \"luma\",\n \"temperature\",\n \"tint\",\n] as const;\n\nexport const COLOR_GRADING_DETAIL_KEYS = [\n \"vignette\",\n \"vignetteMidpoint\",\n \"vignetteRoundness\",\n \"vignetteFeather\",\n \"grain\",\n \"grainSize\",\n \"grainRoughness\",\n] as const;\n\nexport const COLOR_GRADING_EFFECT_KEYS = [\n \"blur\",\n \"pixelate\",\n \"chromaBleed\",\n \"tapeDamage\",\n \"tapeTracking\",\n \"tapeNoise\",\n \"tapeSpeed\",\n \"filmArtifacts\",\n \"halftone\",\n \"halftoneSize\",\n \"twoInkPrint\",\n \"twoInkPrintSize\",\n \"ascii\",\n \"asciiSize\",\n \"asciiInvert\",\n \"asciiStyle\",\n \"asciiColor\",\n \"asciiRotation\",\n \"dither\",\n \"ditherSize\",\n \"bloom\",\n \"bloomRadius\",\n \"monoScreen\",\n \"monoScreenSize\",\n \"monoScreenAngle\",\n \"monoScreenSpread\",\n \"monoScreenShape\",\n \"monoScreenInvert\",\n \"scanlines\",\n \"scanlineCount\",\n \"scanlineSoftness\",\n \"chromaticAberration\",\n \"chromaticAngle\",\n \"crtCurvature\",\n \"digitalGlitch\",\n \"digitalGlitchColorSplit\",\n \"digitalGlitchLineTear\",\n \"digitalGlitchPixelate\",\n \"digitalGlitchBlockAmount\",\n \"digitalGlitchBlockDisplacement\",\n \"digitalGlitchBlockOpacity\",\n \"digitalGlitchSpeed\",\n \"engraving\",\n \"engravingSpacing\",\n \"engravingMinThickness\",\n \"engravingMaxThickness\",\n \"engravingAngle\",\n \"engravingContrast\",\n \"engravingSharpness\",\n \"engravingWave\",\n \"engravingWaveFrequency\",\n \"crosshatch\",\n \"crosshatchSpacing\",\n \"crosshatchThickness\",\n \"crosshatchAngle\",\n \"crosshatchContrast\",\n \"crosshatchEdges\",\n \"crosshatchLineWeight\",\n \"crosshatchWave\",\n \"crosshatchWaveFrequency\",\n \"kuwahara\",\n \"kuwaharaRadius\",\n \"kuwaharaSharpness\",\n \"kuwaharaSaturation\",\n] as const;\n\nexport const COLOR_GRADING_LUT_KEYS = [\"src\", \"intensity\"] as const;\n\ntype NumericLimit = Readonly<{ min: number; max: number }>;\n\nconst UNIT_LIMIT: NumericLimit = COLOR_GRADING_ADVANCED_LIMITS.unit;\nconst SIGNED_UNIT_LIMIT: NumericLimit = COLOR_GRADING_ADVANCED_LIMITS.signedUnit;\nconst EFFECT_LIMIT_OVERRIDES: Readonly<Record<string, NumericLimit>> =\n COLOR_GRADING_ADVANCED_LIMITS.effects;\nconst VARIABLE_REF = /^\\$(?:\\{[A-Za-z0-9_.:-]+\\}|[A-Za-z0-9_.:-]+)$/;\nconst PALETTE_COLOR = /^#[0-9a-f]{6}$/i;\n\nconst OBJECT_SECTIONS = [\n [\"adjust\", COLOR_GRADING_ADJUST_KEYS],\n [\"details\", COLOR_GRADING_DETAIL_KEYS],\n [\"effects\", COLOR_GRADING_EFFECT_KEYS],\n [\"lut\", COLOR_GRADING_LUT_KEYS],\n] as const;\n\nexport interface ColorGradingContractIssue {\n path: string;\n message: string;\n hint?: string;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function isColorGradingVariableRef(value: unknown): value is string {\n return typeof value === \"string\" && VARIABLE_REF.test(value.trim());\n}\n\nfunction unknownKeysHint(path: string, unknown: readonly string[]): string {\n if (path !== \"grading\") return `Correct or remove the unsupported \"${path}\" keys.`;\n const sections = new Set(\n unknown.flatMap((key) =>\n OBJECT_SECTIONS.filter(([, keys]) => (keys as readonly string[]).includes(key)).map(\n ([section]) => section,\n ),\n ),\n );\n return sections.size === 1\n ? `Move those controls under \"${[...sections][0]}\".`\n : \"Use only the documented media-treatment keys at the top level.\";\n}\n\nfunction validateObject(\n value: unknown,\n path: string,\n keys: readonly string[],\n issues: ColorGradingContractIssue[],\n): Record<string, unknown> | null {\n if (isColorGradingVariableRef(value)) return null;\n if (!isRecord(value)) {\n issues.push({ path, message: \"must be an object or variable reference\" });\n return null;\n }\n const allowed = new Set(keys);\n const unknown = Object.keys(value).filter((key) => !allowed.has(key));\n if (unknown.length > 0) {\n issues.push({\n path,\n message: `has unsupported key(s): ${unknown.join(\", \")}`,\n hint: unknownKeysHint(path, unknown),\n });\n }\n return value;\n}\n\nfunction isNumberInRange(value: unknown, limit: NumericLimit, inclusiveMax: boolean): boolean {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value < limit.min) return false;\n return inclusiveMax ? value <= limit.max : value < limit.max;\n}\n\nfunction validateNumericField(\n value: Record<string, unknown>,\n key: string,\n path: string,\n limit: NumericLimit,\n issues: ColorGradingContractIssue[],\n inclusiveMax = true,\n): void {\n const candidate = value[key];\n if (candidate === undefined || isColorGradingVariableRef(candidate)) return;\n if (isNumberInRange(candidate, limit, inclusiveMax)) return;\n issues.push({\n path: path ? `${path}.${key}` : key,\n message: `must be a finite number from ${limit.min} ${inclusiveMax ? \"through\" : \"up to\"} ${limit.max}`,\n });\n}\n\nfunction isFiniteTuple(value: unknown): value is readonly [number, number] {\n return (\n Array.isArray(value) &&\n value.length === 2 &&\n value.every((coordinate) => typeof coordinate === \"number\" && Number.isFinite(coordinate))\n );\n}\n\nfunction validateCurve(value: unknown, path: string, issues: ColorGradingContractIssue[]): void {\n if (isColorGradingVariableRef(value)) return;\n if (!Array.isArray(value) || value.length < 2 || value.length > COLOR_GRADING_MAX_CURVE_POINTS) {\n issues.push({\n path,\n message: `must contain 2 to ${COLOR_GRADING_MAX_CURVE_POINTS} [input, output] points`,\n });\n return;\n }\n\n const inputs = new Set<number>();\n value.forEach((point, index) => {\n if (!isFiniteTuple(point)) {\n issues.push({ path: `${path}[${index}]`, message: \"must be a finite [input, output] tuple\" });\n return;\n }\n const [input, output] = point;\n if (input < 0 || input > 1 || output < 0 || output > 1) {\n issues.push({ path: `${path}[${index}]`, message: \"coordinates must be between 0 and 1\" });\n }\n if (inputs.has(input)) issues.push({ path, message: `contains duplicate input ${input}` });\n inputs.add(input);\n });\n\n const inferredPointCount = value.length + (inputs.has(0) ? 0 : 1) + (inputs.has(1) ? 0 : 1);\n if (inferredPointCount > COLOR_GRADING_MAX_CURVE_POINTS) {\n issues.push({\n path,\n message: `must contain at most ${COLOR_GRADING_MAX_CURVE_POINTS} points including inferred 0 and 1 endpoints`,\n });\n }\n}\n\nfunction validateHueCurve(\n value: unknown,\n path: string,\n outputMin: number,\n outputMax: number,\n issues: ColorGradingContractIssue[],\n): void {\n if (isColorGradingVariableRef(value)) return;\n if (!Array.isArray(value) || value.length < 3 || value.length > COLOR_GRADING_MAX_CURVE_POINTS) {\n issues.push({\n path,\n message: `must contain 3 to ${COLOR_GRADING_MAX_CURVE_POINTS} [hueDegrees, delta] points`,\n });\n return;\n }\n\n const hues = new Set<number>();\n value.forEach((point, index) => {\n if (!isFiniteTuple(point)) {\n issues.push({ path: `${path}[${index}]`, message: \"must be a finite [hue, delta] tuple\" });\n return;\n }\n const [hue, delta] = point;\n if (hue < 0 || hue >= 360) {\n issues.push({ path: `${path}[${index}][0]`, message: \"must be from 0 up to 360 degrees\" });\n }\n if (delta < outputMin || delta > outputMax) {\n issues.push({\n path: `${path}[${index}][1]`,\n message: `must be between ${outputMin} and ${outputMax}`,\n });\n }\n if (hues.has(hue)) issues.push({ path, message: `contains duplicate hue ${hue}` });\n hues.add(hue);\n });\n}\n\nfunction validateNumericSection(\n value: Record<string, unknown>,\n path: string,\n keys: readonly string[],\n limitFor: (key: string) => NumericLimit,\n issues: ColorGradingContractIssue[],\n): void {\n for (const key of keys) validateNumericField(value, key, path, limitFor(key), issues);\n}\n\nfunction validatePalette(value: unknown, issues: ColorGradingContractIssue[]): void {\n if (value === undefined || value === null || isColorGradingVariableRef(value)) return;\n const hint =\n 'Use 2 to 6 colors in the intended mapping order, each written as exact \"#RRGGBB\", or use a project variable reference.';\n if (!Array.isArray(value) || value.length < 2 || value.length > 6) {\n issues.push({ path: \"palette\", message: \"must contain 2 to 6 hex colors\", hint });\n return;\n }\n value.forEach((color, index) => {\n if (typeof color !== \"string\" || !PALETTE_COLOR.test(color)) {\n issues.push({\n path: `palette[${index}]`,\n message: \"must be a six-digit hex color\",\n hint,\n });\n }\n });\n}\n\nfunction validateLut(\n value: unknown,\n object: Record<string, unknown> | null,\n issues: ColorGradingContractIssue[],\n): void {\n if (typeof value === \"string\") {\n if (!value.trim()) issues.push({ path: \"lut\", message: \"must not be empty\" });\n return;\n }\n if (!object) return;\n if (\n !isColorGradingVariableRef(object.src) &&\n (typeof object.src !== \"string\" || !object.src.trim())\n ) {\n issues.push({\n path: \"lut.src\",\n message: \"must be a non-empty string or variable reference\",\n });\n }\n validateNumericField(object, \"intensity\", \"lut\", UNIT_LIMIT, issues);\n}\n\nfunction validateEnabled(\n grading: Record<string, unknown>,\n issues: ColorGradingContractIssue[],\n): void {\n if (\n grading.enabled !== undefined &&\n !isColorGradingVariableRef(grading.enabled) &&\n typeof grading.enabled !== \"boolean\"\n ) {\n issues.push({ path: \"enabled\", message: \"must be a boolean or variable reference\" });\n }\n}\n\nfunction validatePreset(\n grading: Record<string, unknown>,\n issues: ColorGradingContractIssue[],\n): void {\n if (\n grading.preset !== undefined &&\n grading.preset !== null &&\n !isColorGradingVariableRef(grading.preset) &&\n (typeof grading.preset !== \"string\" || !grading.preset.trim())\n ) {\n issues.push({\n path: \"preset\",\n message: \"must be a non-empty string, null, or variable reference\",\n });\n }\n}\n\nfunction validateColorSpace(\n grading: Record<string, unknown>,\n issues: ColorGradingContractIssue[],\n): void {\n if (\n grading.colorSpace !== undefined &&\n !isColorGradingVariableRef(grading.colorSpace) &&\n grading.colorSpace !== COLOR_GRADING_COLOR_SPACE\n ) {\n issues.push({\n path: \"colorSpace\",\n message: `must be \"${COLOR_GRADING_COLOR_SPACE}\" or a variable reference`,\n });\n }\n}\n\nfunction validateTopLevel(\n grading: Record<string, unknown>,\n issues: ColorGradingContractIssue[],\n): void {\n validateEnabled(grading, issues);\n validateNumericField(grading, \"intensity\", \"\", UNIT_LIMIT, issues);\n validatePreset(grading, issues);\n validateColorSpace(grading, issues);\n}\n\nfunction validateSection(\n grading: Record<string, unknown>,\n key: (typeof OBJECT_SECTIONS)[number][0],\n keys: readonly string[],\n issues: ColorGradingContractIssue[],\n): void {\n const section = grading[key];\n if (section === undefined || (key === \"lut\" && section === null)) return;\n if (key === \"lut\" && typeof section === \"string\") {\n validateLut(section, null, issues);\n return;\n }\n const object = validateObject(section, key, keys, issues);\n if (!object) return;\n if (key === \"lut\") return validateLut(section, object, issues);\n\n const limitFor = (control: string): NumericLimit => {\n if (key === \"adjust\" && control === \"exposure\") return { min: -2, max: 2 };\n if (key === \"adjust\" || (key === \"details\" && control === \"vignetteRoundness\")) {\n return SIGNED_UNIT_LIMIT;\n }\n return key === \"effects\" ? (EFFECT_LIMIT_OVERRIDES[control] ?? UNIT_LIMIT) : UNIT_LIMIT;\n };\n validateNumericSection(object, key, keys, limitFor, issues);\n}\n\nfunction validateSections(\n grading: Record<string, unknown>,\n issues: ColorGradingContractIssue[],\n): void {\n for (const [key, keys] of OBJECT_SECTIONS) validateSection(grading, key, keys, issues);\n}\n\nfunction validateWheels(value: unknown, issues: ColorGradingContractIssue[]): void {\n if (value === undefined || isColorGradingVariableRef(value)) return;\n const wheels = validateObject(value, \"wheels\", COLOR_GRADING_WHEEL_KEYS, issues);\n if (!wheels) return;\n\n for (const wheel of COLOR_GRADING_WHEEL_KEYS) {\n if (wheels[wheel] === undefined) continue;\n const path = `wheels.${wheel}`;\n const controls = validateObject(wheels[wheel], path, COLOR_GRADING_WHEEL_CONTROL_KEYS, issues);\n if (!controls) continue;\n validateNumericField(\n controls,\n \"hue\",\n path,\n COLOR_GRADING_ADVANCED_LIMITS.hueDegrees,\n issues,\n COLOR_GRADING_ADVANCED_LIMITS.hueDegrees.inclusiveMax,\n );\n validateNumericField(controls, \"amount\", path, UNIT_LIMIT, issues);\n validateNumericField(controls, \"level\", path, SIGNED_UNIT_LIMIT, issues);\n }\n}\n\nfunction validateCurves(value: unknown, issues: ColorGradingContractIssue[]): void {\n if (value === undefined || isColorGradingVariableRef(value)) return;\n const curves = validateObject(value, \"curves\", COLOR_GRADING_CURVE_KEYS, issues);\n if (!curves) return;\n for (const key of COLOR_GRADING_CURVE_KEYS) {\n if (curves[key] !== undefined) validateCurve(curves[key], `curves.${key}`, issues);\n }\n}\n\nfunction validateHueCurves(value: unknown, issues: ColorGradingContractIssue[]): void {\n if (value === undefined || isColorGradingVariableRef(value)) return;\n const curves = validateObject(value, \"hueCurves\", COLOR_GRADING_HUE_CURVE_KEYS, issues);\n if (!curves) return;\n const limits = {\n hueVsHue: [-180, 180],\n hueVsSaturation: [-1, 1],\n hueVsLuma: [-1, 1],\n } as const;\n for (const key of COLOR_GRADING_HUE_CURVE_KEYS) {\n if (curves[key] !== undefined) {\n validateHueCurve(curves[key], `hueCurves.${key}`, limits[key][0], limits[key][1], issues);\n }\n }\n}\n\nfunction validateSoftRange(\n value: unknown,\n path: string,\n issues: ColorGradingContractIssue[],\n): void {\n const range = validateObject(value, path, COLOR_GRADING_SOFT_RANGE_KEYS, issues);\n if (!range) return;\n validateNumericField(range, \"min\", path, UNIT_LIMIT, issues);\n validateNumericField(range, \"max\", path, UNIT_LIMIT, issues);\n validateNumericField(\n range,\n \"softness\",\n path,\n COLOR_GRADING_ADVANCED_LIMITS.secondarySoftRangeSoftness,\n issues,\n );\n if (typeof range.min === \"number\" && typeof range.max === \"number\" && range.min >= range.max) {\n issues.push({ path, message: \"min must be smaller than max\" });\n }\n}\n\nfunction validateSecondaryHue(\n value: unknown,\n path: string,\n issues: ColorGradingContractIssue[],\n): void {\n const hue = validateObject(value, path, COLOR_GRADING_HUE_RANGE_KEYS, issues);\n if (!hue) return;\n validateNumericField(\n hue,\n \"center\",\n path,\n COLOR_GRADING_ADVANCED_LIMITS.hueDegrees,\n issues,\n COLOR_GRADING_ADVANCED_LIMITS.hueDegrees.inclusiveMax,\n );\n validateNumericField(hue, \"range\", path, COLOR_GRADING_ADVANCED_LIMITS.secondaryHueRange, issues);\n validateNumericField(\n hue,\n \"softness\",\n path,\n COLOR_GRADING_ADVANCED_LIMITS.secondaryHueSoftness,\n issues,\n );\n if (\n typeof hue.range === \"number\" &&\n typeof hue.softness === \"number\" &&\n hue.range + hue.softness > COLOR_GRADING_ADVANCED_LIMITS.secondaryHueCombinedMax\n ) {\n issues.push({\n path,\n message: `range plus softness must not exceed ${COLOR_GRADING_ADVANCED_LIMITS.secondaryHueCombinedMax} degrees`,\n });\n }\n}\n\nfunction validateSecondaryKey(\n value: unknown,\n path: string,\n issues: ColorGradingContractIssue[],\n): void {\n const key = validateObject(value, path, COLOR_GRADING_SECONDARY_KEY_KEYS, issues);\n if (!key) return;\n if (key.hue !== undefined) validateSecondaryHue(key.hue, `${path}.hue`, issues);\n if (key.saturation !== undefined) validateSoftRange(key.saturation, `${path}.saturation`, issues);\n if (key.luma !== undefined) validateSoftRange(key.luma, `${path}.luma`, issues);\n}\n\nfunction validateSecondaryCorrection(\n value: unknown,\n path: string,\n issues: ColorGradingContractIssue[],\n): void {\n const correction = validateObject(value, path, COLOR_GRADING_SECONDARY_CORRECTION_KEYS, issues);\n if (!correction) return;\n validateNumericField(\n correction,\n \"hueShift\",\n path,\n COLOR_GRADING_ADVANCED_LIMITS.secondaryHueShift,\n issues,\n );\n for (const key of [\"saturation\", \"luma\", \"temperature\", \"tint\"] as const) {\n validateNumericField(correction, key, path, SIGNED_UNIT_LIMIT, issues);\n }\n}\n\nfunction validateSecondary(\n value: unknown,\n index: number,\n issues: ColorGradingContractIssue[],\n): void {\n const path = `secondaries[${index}]`;\n const secondary = validateObject(value, path, COLOR_GRADING_SECONDARY_KEYS, issues);\n if (!secondary) return;\n if (\n secondary.enabled !== undefined &&\n typeof secondary.enabled !== \"boolean\" &&\n !isColorGradingVariableRef(secondary.enabled)\n ) {\n issues.push({ path: `${path}.enabled`, message: \"must be a boolean\" });\n }\n validateSecondaryKey(secondary.key, `${path}.key`, issues);\n validateSecondaryCorrection(secondary.correction, `${path}.correction`, issues);\n}\n\nfunction validateSecondaries(value: unknown, issues: ColorGradingContractIssue[]): void {\n if (value === undefined || isColorGradingVariableRef(value)) return;\n if (!Array.isArray(value) || value.length > COLOR_GRADING_MAX_SECONDARIES) {\n issues.push({\n path: \"secondaries\",\n message: `must be an array of at most ${COLOR_GRADING_MAX_SECONDARIES} selections`,\n });\n return;\n }\n\n value.forEach((secondary, index) => validateSecondary(secondary, index, issues));\n}\n\n/** Browser-safe structural validation shared by Lint, CLI, and Core consumers. */\nexport function validateColorGradingContract(value: unknown): ColorGradingContractIssue[] {\n if (typeof value === \"string\") {\n return value.trim() ? [] : [{ path: \"grading\", message: \"is empty\" }];\n }\n\n const issues: ColorGradingContractIssue[] = [];\n const grading = validateObject(value, \"grading\", COLOR_GRADING_TOP_LEVEL_KEYS, issues);\n if (!grading) return issues;\n\n validateTopLevel(grading, issues);\n validateSections(grading, issues);\n validateWheels(grading.wheels, issues);\n validateCurves(grading.curves, issues);\n validateHueCurves(grading.hueCurves, issues);\n validateSecondaries(grading.secondaries, issues);\n validatePalette(grading.palette, issues);\n return issues;\n}\n"],"mappings":"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","names":[]}