@hyperframes/core 0.8.208 → 0.8.210

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/index.d.ts CHANGED
@@ -34,7 +34,7 @@ export { clearGifFramesBeforeNext, gifClearsAfterLeavingFrameInPlace, parseAnima
34
34
  export { HF_COLOR_GRADING_ATTR, HF_COLOR_GRADING_ADJUST_KEYS, HF_COLOR_GRADING_ACTIVE_EFFECT_KEYS, HF_COLOR_GRADING_ANIMATABLE_PROPERTIES, HF_COLOR_GRADING_CANVAS_ID_PREFIX, HF_COLOR_GRADING_COLOR_SPACE, HF_COLOR_GRADING_CURVE_KEYS, HF_COLOR_GRADING_DETAIL_KEYS, HF_COLOR_GRADING_EFFECT_APPLY_DEFAULTS, HF_COLOR_GRADING_EFFECT_KEYS, HF_COLOR_GRADING_EFFECT_PRESETS, HF_COLOR_GRADING_GRADE_PRESETS, HF_COLOR_GRADING_HUE_CURVE_KEYS, HF_COLOR_GRADING_LUT_KEYS, HF_COLOR_GRADING_PALETTES, HF_COLOR_GRADING_PRESETS, HF_COLOR_GRADING_TOP_LEVEL_KEYS, HF_COLOR_GRADING_WHEEL_KEYS, getHfColorGradingCapabilities, hasHfColorGradingAuthoredValues, isHfColorGradingActive, normalizeHfColorGrading, normalizeHfColorGradingWithVariables, resolveHfColorGradingVariables, serializeHfColorGrading, type HfColorGrading, type HfColorGradingAdjust, type HfColorGradingAdjustKey, type HfColorGradingAnimatablePath, type HfColorGradingActiveEffectKey, type HfColorGradingDetailKey, type HfColorGradingDetails, type HfColorGradingEffectKey, type HfColorGradingEffects, type HfColorGradingLutRef, type HfColorGradingPreset, type HfColorGradingPresetId, type HfColorGradingTarget, type HfColorGradingVariableMap, type HfColorGradingCapabilities, type HfColorGradingCurveKey, type HfColorGradingCurves, type HfColorGradingHueCurveKey, type HfColorGradingHueCurves, type HfColorGradingHueRange, type HfColorGradingSecondary, type HfColorGradingSecondaryCorrection, type HfColorGradingSoftRange, type HfColorGradingTonalWheel, type HfColorGradingWheelKey, type HfColorGradingWheels, type HfColorCurvePoint, type HfHueCurvePoint, type NormalizedHfColorGrading, type NormalizedHfColorGradingCurves, type NormalizedHfColorGradingHueCurves, type NormalizedHfColorGradingSecondary, type NormalizedHfColorGradingWheels, type ResolvedHfColorGrading, } from "./colorGrading.js";
35
35
  export { parseCubeLut, CubeLutParseError, type ParseCubeLutOptions } from "./colorLuts.js";
36
36
  export { firstFrameColourArgs, hdrToSdrToneMapFilter, parseFirstFrameColour, type ToneMapSourceColour, } from "./hdrToneMap.js";
37
- export { HEAVY_BITRATE, HEAVY_PIXEL_SHARE, PREVIEW_PROXY_BOX_PARAM, PREVIEW_PROXY_LIGHT_PARAM, PREVIEW_PROXY_PROGRESS_HEADER, PREVIEW_PROXY_RETRY_PARAM, PREVIEW_QUALITY_LEVELS, formatPreviewProxyBox, isHeavyForPreview, isPreviewQuality, parsePreviewProxyBox, quantizePreviewProxyBox, scalePreviewProxyBox, type PreviewProxyBox, type PreviewQuality, type PreviewSourceFacts, } from "./previewProxyBox.js";
37
+ export { HEAVY_BITRATE, HEAVY_PIXEL_SHARE, PREVIEW_PROXY_BOX_PARAM, PREVIEW_PROXY_LIGHT_PARAM, PREVIEW_PROXY_PRIORITY_HEADER, PREVIEW_PROXY_PROGRESS_HEADER, PREVIEW_PROXY_RETRY_PARAM, PREVIEW_QUALITY_LEVELS, formatPreviewProxyBox, isHeavyForPreview, isPreviewQuality, parsePreviewProxyBox, quantizePreviewProxyBox, scalePreviewProxyBox, type PreviewProxyBox, type PreviewQuality, type PreviewSourceFacts, } from "./previewProxyBox.js";
38
38
  export { HYPERFRAME_RUNTIME_ARTIFACTS, HYPERFRAME_RUNTIME_CONTRACT, loadHyperframeRuntimeSource, type HyperframeRuntimeContract, } from "./inline-scripts/hyperframe.js";
39
39
  export { HYPERFRAME_RUNTIME_GLOBALS, HYPERFRAME_BRIDGE_SOURCES, HYPERFRAME_CONTROL_ACTIONS, type HyperframeControlAction, } from "./inline-scripts/runtimeContract.js";
40
40
  export { getHyperframeRuntimeScript } from "./generated/runtime-inline.js";
package/dist/index.js CHANGED
@@ -30,7 +30,7 @@ export { clearGifFramesBeforeNext, gifClearsAfterLeavingFrameInPlace, parseAnima
30
30
  export { HF_COLOR_GRADING_ATTR, HF_COLOR_GRADING_ADJUST_KEYS, HF_COLOR_GRADING_ACTIVE_EFFECT_KEYS, HF_COLOR_GRADING_ANIMATABLE_PROPERTIES, HF_COLOR_GRADING_CANVAS_ID_PREFIX, HF_COLOR_GRADING_COLOR_SPACE, HF_COLOR_GRADING_CURVE_KEYS, HF_COLOR_GRADING_DETAIL_KEYS, HF_COLOR_GRADING_EFFECT_APPLY_DEFAULTS, HF_COLOR_GRADING_EFFECT_KEYS, HF_COLOR_GRADING_EFFECT_PRESETS, HF_COLOR_GRADING_GRADE_PRESETS, HF_COLOR_GRADING_HUE_CURVE_KEYS, HF_COLOR_GRADING_LUT_KEYS, HF_COLOR_GRADING_PALETTES, HF_COLOR_GRADING_PRESETS, HF_COLOR_GRADING_TOP_LEVEL_KEYS, HF_COLOR_GRADING_WHEEL_KEYS, getHfColorGradingCapabilities, hasHfColorGradingAuthoredValues, isHfColorGradingActive, normalizeHfColorGrading, normalizeHfColorGradingWithVariables, resolveHfColorGradingVariables, serializeHfColorGrading, } from "./colorGrading.js";
31
31
  export { parseCubeLut, CubeLutParseError } from "./colorLuts.js";
32
32
  export { firstFrameColourArgs, hdrToSdrToneMapFilter, parseFirstFrameColour, } from "./hdrToneMap.js";
33
- export { HEAVY_BITRATE, HEAVY_PIXEL_SHARE, PREVIEW_PROXY_BOX_PARAM, PREVIEW_PROXY_LIGHT_PARAM, PREVIEW_PROXY_PROGRESS_HEADER, PREVIEW_PROXY_RETRY_PARAM, PREVIEW_QUALITY_LEVELS, formatPreviewProxyBox, isHeavyForPreview, isPreviewQuality, parsePreviewProxyBox, quantizePreviewProxyBox, scalePreviewProxyBox, } from "./previewProxyBox.js";
33
+ export { HEAVY_BITRATE, HEAVY_PIXEL_SHARE, PREVIEW_PROXY_BOX_PARAM, PREVIEW_PROXY_LIGHT_PARAM, PREVIEW_PROXY_PRIORITY_HEADER, PREVIEW_PROXY_PROGRESS_HEADER, PREVIEW_PROXY_RETRY_PARAM, PREVIEW_QUALITY_LEVELS, formatPreviewProxyBox, isHeavyForPreview, isPreviewQuality, parsePreviewProxyBox, quantizePreviewProxyBox, scalePreviewProxyBox, } from "./previewProxyBox.js";
34
34
  // Inline scripts
35
35
  export { HYPERFRAME_RUNTIME_ARTIFACTS, HYPERFRAME_RUNTIME_CONTRACT, loadHyperframeRuntimeSource, } from "./inline-scripts/hyperframe.js";
36
36
  export { HYPERFRAME_RUNTIME_GLOBALS, HYPERFRAME_BRIDGE_SOURCES, HYPERFRAME_CONTROL_ACTIONS, } from "./inline-scripts/runtimeContract.js";
@@ -3,6 +3,7 @@ export declare const PREVIEW_PROXY_BOX_PARAM = "hf-proxy-box";
3
3
  export declare const PREVIEW_PROXY_LIGHT_PARAM = "hf-proxy-light";
4
4
  export declare const PREVIEW_PROXY_RETRY_PARAM = "hf-proxy-retry";
5
5
  export declare const PREVIEW_PROXY_PROGRESS_HEADER = "X-HF-Proxy-Progress";
6
+ export declare const PREVIEW_PROXY_PRIORITY_HEADER = "X-HF-Proxy-Priority";
6
7
  export type PreviewProxyBox = {
7
8
  width: number;
8
9
  height: number;
@@ -3,6 +3,7 @@ export const PREVIEW_PROXY_BOX_PARAM = "hf-proxy-box";
3
3
  export const PREVIEW_PROXY_LIGHT_PARAM = "hf-proxy-light";
4
4
  export const PREVIEW_PROXY_RETRY_PARAM = "hf-proxy-retry";
5
5
  export const PREVIEW_PROXY_PROGRESS_HEADER = "X-HF-Proxy-Progress";
6
+ export const PREVIEW_PROXY_PRIORITY_HEADER = "X-HF-Proxy-Priority";
6
7
  // 256 * 2^(k/4), even: nearby boxes share one cached copy, and a rung overshoots by at most ~19%.
7
8
  const RUNGS = [
8
9
  256, 304, 362, 430, 512, 608, 724, 862, 1024, 1218, 1448, 1722, 2048, 2436, 2896, 3444, 4096,
@@ -0,0 +1,5 @@
1
+ /** Removes a scratch folder or file the mix is already done with. A refusal (Windows temp folders that deny
2
+ * delete) leaves it for the render's own cleanup and never fails the audio it served. */
3
+ export declare function removeScratch(path: string): void;
4
+ /** Runs `run`; whatever removeScratch left behind inside it (this run's only) goes to `report` once it ends. */
5
+ export declare function collectScratchLeftovers<T>(run: () => Promise<T>, report: (left: string[]) => void): Promise<T>;
@@ -0,0 +1,23 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { rmSync } from "node:fs";
3
+ const collecting = new AsyncLocalStorage();
4
+ /** Removes a scratch folder or file the mix is already done with. A refusal (Windows temp folders that deny
5
+ * delete) leaves it for the render's own cleanup and never fails the audio it served. */
6
+ export function removeScratch(path) {
7
+ try {
8
+ rmSync(path, { recursive: true, force: true });
9
+ }
10
+ catch {
11
+ collecting.getStore()?.push(path);
12
+ }
13
+ }
14
+ /** Runs `run`; whatever removeScratch left behind inside it (this run's only) goes to `report` once it ends. */
15
+ export async function collectScratchLeftovers(run, report) {
16
+ const left = [];
17
+ try {
18
+ return await collecting.run(left, run);
19
+ }
20
+ finally {
21
+ report(left);
22
+ }
23
+ }
@@ -0,0 +1,16 @@
1
+ interface Options {
2
+ platform?: NodeJS.Platform;
3
+ /** Copy the bytes when Windows keeps refusing (a TEMP that denies delete). Not atomic: scratch files only. */
4
+ copyFallback?: boolean;
5
+ }
6
+ /** `renameSync` that rides out Windows' transient file locks; other platforms and errors throw at once. */
7
+ export declare function renameWithRetry(from: string, to: string, options?: Options & {
8
+ rename?: (from: string, to: string) => void;
9
+ copy?: (from: string, to: string) => void;
10
+ }): void;
11
+ /** Async twin of {@link renameWithRetry}. */
12
+ export declare function renameWithRetryAsync(from: string, to: string, options?: Options & {
13
+ rename?: (from: string, to: string) => Promise<void>;
14
+ copy?: (from: string, to: string) => Promise<void>;
15
+ }): Promise<void>;
16
+ export {};
@@ -0,0 +1,81 @@
1
+ import { copyFileSync, renameSync } from "node:fs";
2
+ import { copyFile, rename } from "node:fs/promises";
3
+ import { removeScratch } from "./removeScratch.js";
4
+ // Windows reports EPERM/EACCES/EBUSY while antivirus or a just-exited process still holds the
5
+ // file; the hold clears within milliseconds. Total wait before giving up: about 1.3 s.
6
+ const RETRYABLE = new Set(["EPERM", "EACCES", "EBUSY"]);
7
+ const BACKOFF_MS = [10, 20, 40, 80, 160, 320, 640];
8
+ function isRetryable(error, platform) {
9
+ if (platform !== "win32")
10
+ return false;
11
+ return RETRYABLE.has(error?.code ?? "");
12
+ }
13
+ /** What a refused rename does next: wait and try again, fall back to a copy, or give up with the error. */
14
+ function nextStep(error, attempt, options, platform) {
15
+ if (!isRetryable(error, platform))
16
+ return "throw";
17
+ if (attempt < BACKOFF_MS.length)
18
+ return "wait";
19
+ return options.copyFallback ? "copy" : "throw";
20
+ }
21
+ function sleepSync(ms) {
22
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
23
+ }
24
+ /** `renameSync` that rides out Windows' transient file locks; other platforms and errors throw at once. */
25
+ export function renameWithRetry(from, to, options = {}) {
26
+ const doRename = options.rename ?? renameSync;
27
+ const doCopy = options.copy ?? copyFileSync;
28
+ const platform = options.platform ?? process.platform;
29
+ for (let attempt = 0;; attempt++) {
30
+ try {
31
+ doRename(from, to);
32
+ return;
33
+ }
34
+ catch (error) {
35
+ const step = nextStep(error, attempt, options, platform);
36
+ if (step === "throw")
37
+ throw error;
38
+ if (step === "wait") {
39
+ sleepSync(BACKOFF_MS[attempt]);
40
+ continue;
41
+ }
42
+ try {
43
+ doCopy(from, to);
44
+ }
45
+ catch {
46
+ throw error;
47
+ }
48
+ removeScratch(from);
49
+ return;
50
+ }
51
+ }
52
+ }
53
+ /** Async twin of {@link renameWithRetry}. */
54
+ export async function renameWithRetryAsync(from, to, options = {}) {
55
+ const doRename = options.rename ?? rename;
56
+ const doCopy = options.copy ?? copyFile;
57
+ const platform = options.platform ?? process.platform;
58
+ for (let attempt = 0;; attempt++) {
59
+ try {
60
+ await doRename(from, to);
61
+ return;
62
+ }
63
+ catch (error) {
64
+ const step = nextStep(error, attempt, options, platform);
65
+ if (step === "throw")
66
+ throw error;
67
+ if (step === "wait") {
68
+ await new Promise((resolve) => setTimeout(resolve, BACKOFF_MS[attempt]));
69
+ continue;
70
+ }
71
+ try {
72
+ await doCopy(from, to);
73
+ }
74
+ catch {
75
+ throw error;
76
+ }
77
+ removeScratch(from);
78
+ return;
79
+ }
80
+ }
81
+ }
@@ -21,10 +21,10 @@
21
21
  * chain the color-grading runtime uses: `__hfVariablesByComp[compId]` for
22
22
  * inlined sub-compositions, then the top-level merged `getVariables()`.
23
23
  *
24
- * Applied at init AND re-applied after the composition loader inlines
25
- * external / template sub-compositions (their DOM and per-instance scoped
26
- * values don't exist at init). Idempotent: re-applying writes the same
27
- * values.
24
+ * Text is bound before composition scripts run; all of it at init and after
25
+ * the composition loader inlines external / template sub-compositions (their
26
+ * DOM and per-instance scoped values don't exist at init). Idempotent:
27
+ * re-applying writes the same values.
28
28
  */
29
29
  /**
30
30
  * Strip characters that could smuggle additional declarations or markup out of
@@ -45,3 +45,5 @@ export declare function sanitizeCssValue(value: string): string;
45
45
  export declare function unproxiedMediaSrc(el: Element): string | null;
46
46
  /** Applies the bindings in `doc`, or only those inside `within` and `within` itself. */
47
47
  export declare function applyVariableBindings(doc: Document, within?: Element): void;
48
+ /** Binds only text, in `doc` or inside `within`: what composition scripts read before they run. */
49
+ export declare function applyTextBindings(doc: Document, within?: Element): void;
@@ -21,10 +21,10 @@
21
21
  * chain the color-grading runtime uses: `__hfVariablesByComp[compId]` for
22
22
  * inlined sub-compositions, then the top-level merged `getVariables()`.
23
23
  *
24
- * Applied at init AND re-applied after the composition loader inlines
25
- * external / template sub-compositions (their DOM and per-instance scoped
26
- * values don't exist at init). Idempotent: re-applying writes the same
27
- * values.
24
+ * Text is bound before composition scripts run; all of it at init and after
25
+ * the composition loader inlines external / template sub-compositions (their
26
+ * DOM and per-instance scoped values don't exist at init). Idempotent:
27
+ * re-applying writes the same values.
28
28
  */
29
29
  import { readVariablesForElement } from "./variableScope.js";
30
30
  import { isScalarVariableValue as isScalar, isSafeMediaUrl, } from "@hyperframes/parsers/composition";
@@ -154,12 +154,13 @@ function variableSrcFor(el, cache, warn = true) {
154
154
  export function unproxiedMediaSrc(el) {
155
155
  return variableSrcFor(el, new Map(), false) ?? unproxiedSrc(el);
156
156
  }
157
+ const matching = (doc, within, selector) => within
158
+ ? [...(within.matches(selector) ? [within] : []), ...within.querySelectorAll(selector)]
159
+ : Array.from(doc.querySelectorAll(selector));
157
160
  /** Applies the bindings in `doc`, or only those inside `within` and `within` itself. */
158
161
  export function applyVariableBindings(doc, within) {
159
162
  const cache = new Map();
160
- const all = (selector) => within
161
- ? [...(within.matches(selector) ? [within] : []), ...within.querySelectorAll(selector)]
162
- : Array.from(doc.querySelectorAll(selector));
163
+ const all = (selector) => matching(doc, within, selector);
163
164
  const topRoot = within ? null : findTopRoot(doc);
164
165
  applyCssCustomProperties([...(topRoot ? [topRoot] : []), ...all("[data-composition-id]")], cache);
165
166
  for (const el of all("[data-var-src]")) {
@@ -167,12 +168,23 @@ export function applyVariableBindings(doc, within) {
167
168
  if (url !== null && unproxiedSrc(el) !== url)
168
169
  el.setAttribute("src", url);
169
170
  }
170
- for (const el of all("[data-var-text]")) {
171
+ bindText(all("[data-var-text]"), cache);
172
+ }
173
+ /** Binds only text, in `doc` or inside `within`: what composition scripts read before they run. */
174
+ export function applyTextBindings(doc, within) {
175
+ bindText(matching(doc, within, "[data-var-text]"), new Map());
176
+ }
177
+ const textWrittenOnce = new WeakSet();
178
+ /** Writes each binding once: a script may then reshape the text, which a second write would duplicate. */
179
+ function bindText(elements, cache) {
180
+ for (const el of elements) {
171
181
  const id = el.getAttribute("data-var-text")?.trim();
172
- if (!id)
182
+ if (!id || textWrittenOnce.has(el))
173
183
  continue;
174
184
  const value = valuesForElement(el, cache)[id];
175
- if (isScalar(value))
176
- setOwnTextPreservingChildren(el, String(value));
185
+ if (!isScalar(value))
186
+ continue;
187
+ setOwnTextPreservingChildren(el, String(value));
188
+ textWrittenOnce.add(el);
177
189
  }
178
190
  }
@@ -20,18 +20,19 @@
20
20
  * const { title = "Untitled", theme = "light" } = getVariables<MyVars>();
21
21
  */
22
22
  import { cssVariableName, detectSlugCollisions } from "../tokenSlug.js";
23
+ import { scopedVariablesFor } from "./variableScope.js";
23
24
  export function getVariables() {
24
25
  if (typeof document === "undefined")
25
26
  return {};
26
- // Same collection the CSS-variable injection uses: <html> first, then any
27
- // composition element carrying the attribute (later declarers win), then
28
- // render-time overrides.
27
+ // <html> first, then any element carrying the attribute (later declarers win), then render-time
28
+ // overrides. A sub-composition's declaration is its own scope's, read from its scoped table.
29
29
  const declarers = new Set();
30
30
  if (document.documentElement?.hasAttribute("data-composition-variables")) {
31
31
  declarers.add(document.documentElement);
32
32
  }
33
33
  for (const el of Array.from(document.querySelectorAll("[data-composition-variables]"))) {
34
- declarers.add(el);
34
+ if (!scopedVariablesFor(el))
35
+ declarers.add(el);
35
36
  }
36
37
  const declaredDefaults = {};
37
38
  for (const el of declarers) {
@@ -5,4 +5,6 @@
5
5
  * runtime consumer of variables (color grading, declarative bindings) so the
6
6
  * scope chain can never diverge between channels.
7
7
  */
8
+ /** The per-instance values of the inlined sub-composition an element belongs to, if any. */
9
+ export declare function scopedVariablesFor(element: Element): Record<string, unknown> | undefined;
8
10
  export declare function readVariablesForElement(element: Element): Record<string, unknown>;
@@ -5,11 +5,17 @@
5
5
  * runtime consumer of variables (color grading, declarative bindings) so the
6
6
  * scope chain can never diverge between channels.
7
7
  */
8
- export function readVariablesForElement(element) {
9
- const win = window;
8
+ /** The per-instance values of the inlined sub-composition an element belongs to, if any. */
9
+ export function scopedVariablesFor(element) {
10
10
  const scope = element.closest("[data-composition-id]");
11
11
  const compositionId = scope?.getAttribute("data-composition-id")?.trim() ?? "";
12
- const scoped = compositionId ? win.__hfVariablesByComp?.[compositionId] : undefined;
12
+ return compositionId
13
+ ? window.__hfVariablesByComp?.[compositionId]
14
+ : undefined;
15
+ }
16
+ export function readVariablesForElement(element) {
17
+ const win = window;
18
+ const scoped = scopedVariablesFor(element);
13
19
  if (scoped)
14
20
  return scoped;
15
21
  const fromHelper = win.__hyperframes?.getVariables?.();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/core",
3
- "version": "0.8.208",
3
+ "version": "0.8.210",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
@@ -64,6 +64,14 @@
64
64
  "import": "./dist/removeTree.js",
65
65
  "types": "./dist/removeTree.d.ts"
66
66
  },
67
+ "./rename-retry": {
68
+ "import": "./dist/renameWithRetry.js",
69
+ "types": "./dist/renameWithRetry.d.ts"
70
+ },
71
+ "./remove-scratch": {
72
+ "import": "./dist/removeScratch.js",
73
+ "types": "./dist/removeScratch.d.ts"
74
+ },
67
75
  "./project-folders": {
68
76
  "import": "./dist/projectFolders.js",
69
77
  "types": "./dist/projectFolders.d.ts"
@@ -417,9 +425,9 @@
417
425
  "linkedom": "^0.18.12",
418
426
  "postcss": "^8.5.8",
419
427
  "wawoff2": "^2.0.1",
420
- "@hyperframes/lint": "0.8.208",
421
- "@hyperframes/studio-server": "0.8.208",
422
- "@hyperframes/parsers": "0.8.208"
428
+ "@hyperframes/lint": "0.8.210",
429
+ "@hyperframes/studio-server": "0.8.210",
430
+ "@hyperframes/parsers": "0.8.210"
423
431
  },
424
432
  "devDependencies": {
425
433
  "@fontsource/archivo-black": "^5.2.8",