@hyperframes/core 0.8.166 → 0.8.168

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.
@@ -97,11 +97,8 @@ export function isMemberGroupHidden(doc, el) {
97
97
  * a sub-composition's root is skipped, so repeating one cannot stack chains.
98
98
  */
99
99
  export function resolveMasterBusElement(root) {
100
- for (const el of root.querySelectorAll("[data-composition-id]")) {
101
- if (!el.parentElement?.closest("[data-composition-id]"))
102
- return el;
103
- }
104
- return null;
100
+ const first = root.querySelector("[data-composition-id]");
101
+ return first && !first.parentElement?.closest("[data-composition-id]") ? first : null;
105
102
  }
106
103
  /** The master bus chain: the top-level root's `data-fx-chain`, if it carries one. */
107
104
  export function readMasterFxChain(root) {
@@ -142,3 +142,5 @@ export interface NestedCompositionHosts<TElement> {
142
142
  * `src`, so a top-level host is enumerated with `[itsOwnSrc]`.
143
143
  */
144
144
  export declare function enumerateNestedCompositionHosts<TElement extends AssemblyAttributed>(assembledHost: AssemblyQueryable<TElement>, ancestry: readonly string[]): NestedCompositionHosts<TElement>;
145
+ /** A mount sharing the page root's id counts as a repeat, so it is numbered instead of taking the root's timeline. */
146
+ export declare function countPageRoot(counts: Map<string, number>, hosts: readonly Element[]): void;
@@ -154,3 +154,11 @@ export function enumerateNestedCompositionHosts(assembledHost, ancestry) {
154
154
  }
155
155
  return { hosts, skipped };
156
156
  }
157
+ /** A mount sharing the page root's id counts as a repeat, so it is numbered instead of taking the root's timeline. */
158
+ export function countPageRoot(counts, hosts) {
159
+ const root = hosts[0]?.ownerDocument.querySelector("[data-composition-id]");
160
+ const rootId = root && !hosts.includes(root) ? root.getAttribute("data-composition-id") : null;
161
+ const mounts = rootId ? counts.get(rootId) : undefined;
162
+ if (rootId && mounts)
163
+ counts.set(rootId, mounts + 1);
164
+ }
@@ -343,10 +343,8 @@ export function wrapScopedCompositionScript(source, compositionId, errorLabel =
343
343
  if (prop !== __hfCompId) {
344
344
  return Reflect.get(target, prop, target);
345
345
  }
346
- var authoredValue = Reflect.get(target, prop, target);
347
- return authoredValue === undefined
348
- ? Reflect.get(target, __hfTimelineCompId, target)
349
- : authoredValue;
346
+ var runtimeValue = Reflect.get(target, __hfTimelineCompId, target);
347
+ return runtimeValue === undefined ? Reflect.get(target, prop, target) : runtimeValue;
350
348
  },
351
349
  set: function(target, prop, value, receiver) {
352
350
  if (prop !== __hfCompId) {
@@ -354,11 +352,15 @@ export function wrapScopedCompositionScript(source, compositionId, errorLabel =
354
352
  }
355
353
  // The authored node remains in the compiled DOM when its local id
356
354
  // differs from the runtime mount id, so readiness legitimately sees
357
- // both compositions. Publish the same timeline under both identities
358
- // instead of replacing one with the other.
359
- var authoredSet = Reflect.set(target, __hfCompId, value, target);
355
+ // both compositions. Publish the same timeline under both identities,
356
+ // unless another mount of the same file already owns the authored one.
357
+ var authoredValue = Reflect.get(target, __hfCompId, target);
358
+ var ownsAuthored =
359
+ authoredValue === undefined ||
360
+ authoredValue === Reflect.get(target, __hfTimelineCompId, target);
360
361
  var runtimeSet = Reflect.set(target, __hfTimelineCompId, value, target);
361
- return authoredSet && runtimeSet;
362
+ if (!ownsAuthored) return runtimeSet;
363
+ return Reflect.set(target, __hfCompId, value, target) && runtimeSet;
362
364
  },
363
365
  });
364
366
  }
@@ -1,6 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { compositionStyle, cssStyleMergeKey, deferScriptsUntilFonts, UNCONDITIONAL_CSS_KEY, headStyleRuns, INLINED_FILE_ATTR, inlineScriptRuns, styleElementsFor, } from "./scriptRuns.js";
3
- import { executableScripts, isJavaScriptType, planCompositionAssembly, } from "./compositionAssembly.js";
3
+ import { countPageRoot, executableScripts, isJavaScriptType, planCompositionAssembly, } from "./compositionAssembly.js";
4
4
  import { SCENE_PART_ATTR } from "../sceneParts.js";
5
5
  import { ensureExternalScriptTag, readExternalScriptAttributes, } from "./externalScripts.js";
6
6
  import { emitMountedModuleScripts, parseImportMap } from "./importMaps.js";
@@ -16,7 +16,7 @@ import { resolve, relative, dirname, isAbsolute, sep } from "path";
16
16
  import { extractStandaloneEntryFromIndex, isTemplateEntry } from "./standaloneEntry.js";
17
17
  import { decodeCssEscapes, decodeWellFormedEscapes, encodeUrlPath, decodedUrlPath, splitUrlSuffix, } from "@hyperframes/parsers/asset-paths";
18
18
  import { CSS_URL_RE, isNonRelativeUrl } from "./assetPaths.js";
19
- import { transformSync } from "esbuild";
19
+ import { transform } from "esbuild";
20
20
  import { compileHtml } from "./htmlCompiler.js";
21
21
  import { RUNTIME_BOOTSTRAP_ATTR, escapeInlineScriptSource, insertRuntimeTag, parseHTMLContent, stripEmbeddedRuntimeScripts, } from "./htmlDocument.js";
22
22
  // rewriteSubCompPaths functions are used by inlineSubCompositions (shared module)
@@ -481,6 +481,7 @@ function countBundledAuthoredCompositionIds(hosts) {
481
481
  continue;
482
482
  counts.set(authoredCompositionId, (counts.get(authoredCompositionId) || 0) + 1);
483
483
  }
484
+ countPageRoot(counts, hosts);
484
485
  return counts;
485
486
  }
486
487
  // fallow-ignore-next-line complexity
@@ -651,7 +652,7 @@ function pushRun(runs, scene, chunk) {
651
652
  else
652
653
  runs.push({ scene, chunks: [chunk] });
653
654
  }
654
- function coalesceHeadStylesAndBodyScripts(document, provenance) {
655
+ async function coalesceHeadStylesAndBodyScripts(document, provenance) {
655
656
  const allHeadStyles = [...document.querySelectorAll("head style")];
656
657
  const isScenePart = (el) => el.hasAttribute(SCENE_PART_ATTR);
657
658
  for (const run of allHeadStyles.length > 1 ? headStyleRuns(allHeadStyles, isScenePart) : []) {
@@ -669,7 +670,7 @@ function coalesceHeadStylesAndBodyScripts(document, provenance) {
669
670
  const mergedJs = joinJsChunks(members.map((el) => el.textContent || ""));
670
671
  if (mergedJs && !parsesAsScript(mergedJs)) {
671
672
  for (const el of members)
672
- el.textContent = inlineScriptSource(el.textContent || "", provenance);
673
+ el.textContent = await inlineScriptSource(el.textContent || "", provenance);
673
674
  continue;
674
675
  }
675
676
  for (const el of members)
@@ -677,7 +678,7 @@ function coalesceHeadStylesAndBodyScripts(document, provenance) {
677
678
  if (!mergedJs)
678
679
  continue;
679
680
  const inlineScript = document.createElement("script");
680
- inlineScript.textContent = inlineScriptSource(mergedJs, provenance);
681
+ inlineScript.textContent = await inlineScriptSource(mergedJs, provenance);
681
682
  if (anchor)
682
683
  anchor.before(inlineScript);
683
684
  else
@@ -740,11 +741,11 @@ export function parsesAsScript(source) {
740
741
  return false;
741
742
  }
742
743
  }
743
- function stripJsCommentsParserSafe(source, provenance) {
744
+ async function stripJsCommentsParserSafe(source, provenance) {
744
745
  if (!source)
745
746
  return source;
746
747
  try {
747
- const result = transformSync(source, {
748
+ const result = await transform(source, {
748
749
  loader: "js",
749
750
  minify: false,
750
751
  legalComments: "none",
@@ -763,8 +764,8 @@ function stripJsCommentsParserSafe(source, provenance) {
763
764
  return source;
764
765
  }
765
766
  }
766
- function inlineScriptSource(js, provenance) {
767
- return escapeInlineScriptSource(stripJsCommentsParserSafe(js, provenance));
767
+ async function inlineScriptSource(js, provenance) {
768
+ return escapeInlineScriptSource(await stripJsCommentsParserSafe(js, provenance));
768
769
  }
769
770
  function preserveLocalScriptIntegrity(doc, src, resolvePath) {
770
771
  const path = resolvePath(src);
@@ -1131,7 +1132,7 @@ async function bundleProject(projectDir, options) {
1131
1132
  if (scene)
1132
1133
  script.setAttribute(SCENE_PART_ATTR, scene);
1133
1134
  script.textContent = skipsRunMerge
1134
- ? inlineScriptSource(text, options?.scriptProvenance)
1135
+ ? await inlineScriptSource(text, options?.scriptProvenance)
1135
1136
  : text;
1136
1137
  document.body.appendChild(script);
1137
1138
  }
@@ -1140,10 +1141,10 @@ async function bundleProject(projectDir, options) {
1140
1141
  emitRootCompositionVariableStyles(document, compVariablesByComp);
1141
1142
  enforceCompositionPixelSizing(document);
1142
1143
  autoHealMissingCompositionIds(document);
1143
- coalesceHeadStylesAndBodyScripts(document, options?.scriptProvenance);
1144
+ await coalesceHeadStylesAndBodyScripts(document, options?.scriptProvenance);
1144
1145
  for (const el of document.querySelectorAll(`script[${INLINED_FILE_ATTR}]`)) {
1145
1146
  const js = el.textContent ?? "";
1146
- el.textContent = escapeInlineScriptSource(isJavaScriptType(el) ? stripJsCommentsParserSafe(js, options?.scriptProvenance) : js);
1147
+ el.textContent = escapeInlineScriptSource(isJavaScriptType(el) ? await stripJsCommentsParserSafe(js, options?.scriptProvenance) : js);
1147
1148
  }
1148
1149
  deferScriptsUntilFonts(document, (el) => el.hasAttribute(RUNTIME_BOOTSTRAP_ATTR));
1149
1150
  injectTextRenderingRule(document);
@@ -65,6 +65,10 @@ export declare function parseFps(input: string | number): FpsParseResult;
65
65
  * errors — those still surface via the discriminated result.
66
66
  */
67
67
  export declare function parseFpsWithDefault(input: string | number | undefined): FpsParseResult;
68
+ /** Integer rates the distributed renderer (Lambda, Cloud Run) accepts; fractional fps renders in-process only. */
69
+ export declare const DISTRIBUTED_FPS: readonly [24, 25, 30, 60];
70
+ export type DistributedFps = (typeof DISTRIBUTED_FPS)[number];
71
+ export declare function isDistributedFps(value: unknown): value is DistributedFps;
68
72
  /** Video orientation / aspect ratio. */
69
73
  export type Orientation = "16:9" | "9:16";
70
74
  export type { Asset, TimelineElementType, MediaElementType, TimelineElementBase, TimelineMediaElement, WaveformData, TimelineTextElement, TimelineCompositionElement, CompositionVariableType, CompositionVariableBase, StringVariable, NumberVariable, ColorVariable, BooleanVariable, EnumVariable, CompositionVariable, CompositionSpec, TimelineElement, MediaFile, CanvasResolution, CompositionAPI, PlayerAPI, AddElementData, ValidationResult, CompositionAsset, Keyframe, KeyframeProperties, ElementKeyframes, StageZoom, StageZoomKeyframe, } from "@hyperframes/parsers";
@@ -101,4 +101,9 @@ export function parseFpsWithDefault(input) {
101
101
  return { ok: true, value: { num: 30, den: 1 } };
102
102
  return parseFps(input);
103
103
  }
104
+ /** Integer rates the distributed renderer (Lambda, Cloud Run) accepts; fractional fps renders in-process only. */
105
+ export const DISTRIBUTED_FPS = [24, 25, 30, 60];
106
+ export function isDistributedFps(value) {
107
+ return DISTRIBUTED_FPS.includes(value);
108
+ }
104
109
  export { CANVAS_DIMENSIONS, VALID_CANVAS_RESOLUTIONS, normalizeResolutionFlag, isAspectAgnosticResolutionAlias, resolveResolutionFlagPair, checkOutputResolutionCompatibility, suggestMatchingPreset, COMPOSITION_VARIABLE_TYPES, TIMELINE_COLORS, DEFAULT_DURATIONS, getDefaultStageZoom, isTextElement, isMediaElement, isCompositionElement, } from "@hyperframes/parsers";