hyperframes 0.8.48 → 0.8.50

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.
@@ -55501,7 +55501,7 @@ function buildChromeArgs(options, config) {
55501
55501
  // frame-capture loop then blocks waiting for it, deadlocking the render.
55502
55502
  "--autoplay-policy=no-user-gesture-required"
55503
55503
  ];
55504
- if (browserGpuMode !== "software") {
55504
+ if (browserGpuMode !== "software" || options.requiresWebGpu) {
55505
55505
  chromeArgs.push(WEBGPU_FLAG);
55506
55506
  }
55507
55507
  if (browserGpuMode === "software") {
@@ -55525,6 +55525,28 @@ function buildChromeArgs(options, config) {
55525
55525
  }
55526
55526
  return chromeArgs;
55527
55527
  }
55528
+ function compositionRequiresWebGpu(html) {
55529
+ for (const [tag] of html.matchAll(/<(?:[^<>"']|"[^"]*"|'[^']*')*>/g)) {
55530
+ if (/\bdata-composition-id\b/i.test(tag)) return /\bdata-requires-webgpu(?:\s|=|>)/i.test(tag);
55531
+ }
55532
+ return false;
55533
+ }
55534
+ async function assertWebGpuAdapterAvailable(page, requiresWebGpu) {
55535
+ if (!requiresWebGpu) return;
55536
+ const hasUsableAdapter = await page.evaluate(async (timeoutMs) => {
55537
+ if (typeof navigator === "undefined" || !navigator.gpu) return false;
55538
+ try {
55539
+ const adapter2 = await Promise.race([
55540
+ navigator.gpu.requestAdapter(),
55541
+ new Promise((resolve18) => setTimeout(() => resolve18(null), timeoutMs))
55542
+ ]);
55543
+ return !!adapter2 && !adapter2.info.isFallbackAdapter;
55544
+ } catch {
55545
+ return false;
55546
+ }
55547
+ }, WEBGPU_ADAPTER_PROBE_TIMEOUT_MS);
55548
+ if (!hasUsableAdapter) throw new WebGpuUnavailableError();
55549
+ }
55528
55550
  function getBrowserGpuArgs(mode, platform3) {
55529
55551
  if (mode === "software") {
55530
55552
  return ["--use-gl=angle", "--use-angle=swiftshader", "--enable-unsafe-swiftshader"];
@@ -55549,7 +55571,7 @@ function getBrowserGpuArgs(mode, platform3) {
55549
55571
  return ["--enable-gpu-rasterization"];
55550
55572
  }
55551
55573
  }
55552
- var _puppeteer, CACHED_HEADLESS_SHELL_EXECUTABLES, ENABLE_BROWSER_POOL, BEGINFRAME_ONLY_FLAGS, BEGINFRAME_SCREENSHOT_PROBE_ATTEMPTS, BEGINFRAME_PROBE_TIMEOUT_MS, BEGINFRAME_PROBE_CLEANUP_TIMEOUT_MS, _autoBrowserGpuModeCache, _unverifiedHardwareGpuWarned, browserLeasePool, _cachedVramMb, CANVAS_DRAW_ELEMENT_FEATURE_FLAG, WEBGPU_FLAG;
55574
+ var _puppeteer, CACHED_HEADLESS_SHELL_EXECUTABLES, ENABLE_BROWSER_POOL, BEGINFRAME_ONLY_FLAGS, BEGINFRAME_SCREENSHOT_PROBE_ATTEMPTS, BEGINFRAME_PROBE_TIMEOUT_MS, BEGINFRAME_PROBE_CLEANUP_TIMEOUT_MS, _autoBrowserGpuModeCache, _unverifiedHardwareGpuWarned, browserLeasePool, _cachedVramMb, CANVAS_DRAW_ELEMENT_FEATURE_FLAG, WEBGPU_FLAG, WebGpuUnavailableError, WEBGPU_ADAPTER_PROBE_TIMEOUT_MS;
55553
55575
  var init_browserManager = __esm({
55554
55576
  "../engine/src/services/browserManager.ts"() {
55555
55577
  "use strict";
@@ -55588,6 +55610,15 @@ var init_browserManager = __esm({
55588
55610
  _cachedVramMb = null;
55589
55611
  CANVAS_DRAW_ELEMENT_FEATURE_FLAG = "--enable-features=CanvasDrawElement";
55590
55612
  WEBGPU_FLAG = "--enable-unsafe-webgpu";
55613
+ WebGpuUnavailableError = class extends Error {
55614
+ constructor() {
55615
+ super(
55616
+ "This composition declares data-requires-webgpu, but no hardware WebGPU adapter could be obtained on this browser launch (a software fallback adapter such as swiftshader reports one but cannot render it). Run on a host with a GPU, or remove data-requires-webgpu."
55617
+ );
55618
+ this.name = "WebGpuUnavailableError";
55619
+ }
55620
+ };
55621
+ WEBGPU_ADAPTER_PROBE_TIMEOUT_MS = 1e4;
55591
55622
  }
55592
55623
  });
55593
55624
 
@@ -59198,8 +59229,9 @@ async function createCaptureSession(serverUrl, outputDir, options, onBeforeCaptu
59198
59229
  config
59199
59230
  );
59200
59231
  const preMode = headlessShell && isLinux && !effectiveForceScreenshot && !supersampling && !drawElementTransparent ? "beginframe" : "screenshot";
59232
+ const requiresWebGpu = options.requiresWebGpu ?? await fetch(`${serverUrl}/index.html`, { signal: AbortSignal.timeout(5e3) }).then((res) => res.text()).then(compositionRequiresWebGpu).catch(() => false);
59201
59233
  const chromeArgs = buildChromeArgs(
59202
- { width: options.width, height: options.height, captureMode: preMode },
59234
+ { width: options.width, height: options.height, captureMode: preMode, requiresWebGpu },
59203
59235
  { ...config, browserGpuMode: resolvedGpuMode }
59204
59236
  );
59205
59237
  const browserLease = await acquireBrowser(chromeArgs, config);
@@ -59210,7 +59242,8 @@ async function createCaptureSession(serverUrl, outputDir, options, onBeforeCaptu
59210
59242
  options,
59211
59243
  onBeforeCapture,
59212
59244
  config,
59213
- useDrawElement
59245
+ useDrawElement,
59246
+ requiresWebGpu
59214
59247
  });
59215
59248
  }
59216
59249
  async function constructCaptureSessionWithRollback(input) {
@@ -59259,6 +59292,7 @@ async function constructCaptureSession(input) {
59259
59292
  onBeforeCapture,
59260
59293
  config,
59261
59294
  useDrawElement,
59295
+ requiresWebGpu,
59262
59296
  onPageCreated
59263
59297
  } = input;
59264
59298
  const { browser, captureMode } = browserLease;
@@ -59321,6 +59355,7 @@ async function constructCaptureSession(input) {
59321
59355
  serverUrl,
59322
59356
  outputDir,
59323
59357
  onBeforeCapture,
59358
+ requiresWebGpu,
59324
59359
  isInitialized: false,
59325
59360
  browserConsoleBuffer: [],
59326
59361
  scriptLoadFailures: [],
@@ -59367,6 +59402,16 @@ function formatConsoleDiagnostic(type, text, locationUrl) {
59367
59402
  }
59368
59403
  function buildZeroDurationDiagnostic(diag) {
59369
59404
  const hints = [];
59405
+ if (diag.pendingBuildReadyKeys.length > 0) {
59406
+ hints.push(
59407
+ `window.__hf.buildReady never resolved for: ${diag.pendingBuildReadyKeys.join(", ")}. The runtime holds render-ready until every registered buildReady promise settles \u2014 find where the composition registers that key and confirm it actually resolves.`
59408
+ );
59409
+ }
59410
+ if (diag.rejectedBuildReadyKeys.length > 0) {
59411
+ hints.push(
59412
+ `window.__hf.buildReady rejected for: ${diag.rejectedBuildReadyKeys.join(", ")}. That key's build promise failed rather than hanging \u2014 find why it rejects.`
59413
+ );
59414
+ }
59370
59415
  if (!diag.hasPlayer) {
59371
59416
  hints.push("window.__player was never set \u2014 the HyperFrames runtime did not initialize.");
59372
59417
  }
@@ -59764,6 +59809,7 @@ async function initializeSession(session) {
59764
59809
  );
59765
59810
  throw error;
59766
59811
  }
59812
+ await assertWebGpuAdapterAvailable(page, session.requiresWebGpu ?? false);
59767
59813
  };
59768
59814
  if (session.captureMode === "screenshot") {
59769
59815
  await gotoEntryPage();
@@ -61198,7 +61244,7 @@ var init_frameCapture = __esm({
61198
61244
  BEGIN_FRAME_CAPTURE_HEADROOM_INTERVALS = 10;
61199
61245
  BEGIN_FRAME_COMMIT_LEAD_INTERVALS = 6;
61200
61246
  BEGIN_FRAME_PROBE_LEAD_INTERVALS = 5;
61201
- HF_READY_DIAGNOSTIC_EXPR = `(function() {
61247
+ HF_READY_DIAGNOSTIC_EXPR = `(async function() {
61202
61248
  var hf = window.__hf;
61203
61249
  var player = window.__player;
61204
61250
  var renderReady = !!window.__renderReady;
@@ -61207,6 +61253,26 @@ var init_frameCapture = __esm({
61207
61253
  var hasTimeline = !!(window.__timelines && Object.keys(window.__timelines).length > 0);
61208
61254
  var root = document.querySelector("[data-composition-id]");
61209
61255
  var declaredDuration = root ? Number(root.getAttribute("data-duration")) : -1;
61256
+ var registry = (window.__hf && window.__hf.buildReady) || {};
61257
+ var keys = Object.keys(registry);
61258
+ // A microtask race mis-sorts a non-native thenable (extra adoption tick)
61259
+ // and rejects the whole expression on a rejected entry. Settling each
61260
+ // key onto its own record and waiting one macrotask avoids both.
61261
+ var settled = {};
61262
+ if (keys.length > 0) {
61263
+ keys.forEach(function(key) {
61264
+ Promise.resolve(registry[key]).then(
61265
+ function() { settled[key] = "resolved"; },
61266
+ function() { settled[key] = "rejected"; },
61267
+ );
61268
+ });
61269
+ await new Promise(function(r) { setTimeout(r, 0); });
61270
+ }
61271
+ // A thenable resolving via its own setTimeout(0) can still misreport as
61272
+ // pending here (macrotask registration order, not resolution order) \u2014 a
61273
+ // narrow case that self-corrects on the next ~1s diagnostic tick.
61274
+ var pendingBuildReadyKeys = keys.filter(function(key) { return !settled[key]; });
61275
+ var rejectedBuildReadyKeys = keys.filter(function(key) { return settled[key] === "rejected"; });
61210
61276
  return {
61211
61277
  renderReady: renderReady,
61212
61278
  hasHf: !!hf,
@@ -61215,6 +61281,8 @@ var init_frameCapture = __esm({
61215
61281
  duration: duration,
61216
61282
  hasTimeline: hasTimeline,
61217
61283
  declaredDuration: declaredDuration,
61284
+ pendingBuildReadyKeys: pendingBuildReadyKeys,
61285
+ rejectedBuildReadyKeys: rejectedBuildReadyKeys,
61218
61286
  };
61219
61287
  })()`;
61220
61288
  LIVE_MAP_MARKERS = [
@@ -64557,6 +64625,37 @@ var init_htmlCompiler = __esm({
64557
64625
  }
64558
64626
  });
64559
64627
 
64628
+ // ../core/dist/compiler/scriptRuns.js
64629
+ function isClassicInline(el) {
64630
+ const type = (el.getAttribute("type") || "").trim().toLowerCase();
64631
+ return !type || type === "text/javascript" || type === "application/javascript";
64632
+ }
64633
+ function isSeparateExecution(el, isPinned) {
64634
+ return el.hasAttribute("src") || isPinned(el) || (el.getAttribute("type") || "").trim().toLowerCase() === "module";
64635
+ }
64636
+ function inlineScriptRuns(scripts, isPinned = () => false) {
64637
+ const runs = [];
64638
+ let members = [];
64639
+ for (const el of scripts) {
64640
+ if (isSeparateExecution(el, isPinned)) {
64641
+ if (members.length === 0)
64642
+ continue;
64643
+ runs.push({ members, anchor: el });
64644
+ members = [];
64645
+ } else if (isClassicInline(el)) {
64646
+ members.push(el);
64647
+ }
64648
+ }
64649
+ if (members.length > 0)
64650
+ runs.push({ members, anchor: null });
64651
+ return runs;
64652
+ }
64653
+ var init_scriptRuns = __esm({
64654
+ "../core/dist/compiler/scriptRuns.js"() {
64655
+ "use strict";
64656
+ }
64657
+ });
64658
+
64560
64659
  // ../core/dist/compiler/externalScripts.js
64561
64660
  function readExternalScriptAttributes(el) {
64562
64661
  const attributes2 = {};
@@ -65200,8 +65299,15 @@ function wrapScopedCompositionScript(source, compositionId, errorLabel = "[Hyper
65200
65299
  })
65201
65300
  : window;
65202
65301
  var __hfResolveGsapTarget = function(target) {
65203
- if (typeof target !== "string") return target;
65204
- return __hfQueryAll(target);
65302
+ if (typeof target === "string") return __hfQueryAll(target);
65303
+ if (!Array.isArray(target)) return target;
65304
+ return target.reduce(function(resolved, item) {
65305
+ if (typeof item === "string") {
65306
+ return resolved.concat(Array.prototype.slice.call(__hfQueryAll(item)));
65307
+ }
65308
+ resolved.push(item);
65309
+ return resolved;
65310
+ }, []);
65205
65311
  };
65206
65312
  var __hfScopeTimeline = function(timeline) {
65207
65313
  if (!timeline || timeline.__hfScopedCompositionRoot === __hfFindRoot()) return timeline;
@@ -68956,6 +69062,46 @@ var require_dist = __commonJS({
68956
69062
  }
68957
69063
  });
68958
69064
 
69065
+ // ../parsers/dist/topLevelElements.js
69066
+ function isSubCompositionHost(node) {
69067
+ return node.attrs["data-composition-src"] !== void 0 || node.attrs["data-composition-id"] !== void 0;
69068
+ }
69069
+ function isTimedElement(node) {
69070
+ const { attrs } = node;
69071
+ return attrs["data-start"] !== void 0 || attrs["data-track-index"] !== void 0 && attrs["data-duration"] !== void 0 || MEDIA_TAGS.has(node.tag.toLowerCase());
69072
+ }
69073
+ function topLevelElements(root) {
69074
+ const rows = [];
69075
+ const visit = (node) => {
69076
+ if (NON_RENDERED_TAGS.has(node.tag.toLowerCase())) return;
69077
+ if (isSubCompositionHost(node) || isTimedElement(node)) rows.push(node);
69078
+ else node.children.forEach(visit);
69079
+ };
69080
+ root.children.forEach(visit);
69081
+ return rows;
69082
+ }
69083
+ function trackKindOf(node) {
69084
+ const explicit = node.attrs["data-track-kind"]?.trim().toLowerCase();
69085
+ if (explicit && TRACK_KINDS.includes(explicit))
69086
+ return { kind: explicit, source: "attribute" };
69087
+ const tag = node.tag.toLowerCase();
69088
+ if (tag === "video" || tag === "audio") return { kind: tag, source: "tag" };
69089
+ if (isSubCompositionHost(node)) {
69090
+ const legacy = node.attrs["data-composition-id"] === "captions" || /(?:^|\s)caption[-_]/.test(node.attrs.class ?? "");
69091
+ return legacy ? { kind: "captions", source: "legacy-captions" } : { kind: "graphics", source: "sub-composition" };
69092
+ }
69093
+ return { kind: "graphics", source: "tag" };
69094
+ }
69095
+ var NON_RENDERED_TAGS, MEDIA_TAGS, TRACK_KINDS;
69096
+ var init_topLevelElements = __esm({
69097
+ "../parsers/dist/topLevelElements.js"() {
69098
+ "use strict";
69099
+ NON_RENDERED_TAGS = /* @__PURE__ */ new Set(["script", "style", "template", "noscript", "link", "meta"]);
69100
+ MEDIA_TAGS = /* @__PURE__ */ new Set(["video", "audio", "img"]);
69101
+ TRACK_KINDS = ["video", "audio", "captions", "graphics"];
69102
+ }
69103
+ });
69104
+
68959
69105
  // ../parsers/dist/assetResolution.js
68960
69106
  function hasUnresolvedTemplatingToken(url) {
68961
69107
  return /<<[^<>]+>>|\{\{[^{}]+\}\}|\$\{[^{}]+\}/.test(url);
@@ -73081,6 +73227,94 @@ function extractScenesFromClips(ctx) {
73081
73227
  collectCompositionIdScenes(ctx, seen, scenes);
73082
73228
  return scenes;
73083
73229
  }
73230
+ function toNode(open) {
73231
+ const attrs = {};
73232
+ for (const name of NODE_ATTRS) attrs[name] = readDecodedAttr(open.raw, name) ?? void 0;
73233
+ return { tag: open.name, attrs, children: [], open };
73234
+ }
73235
+ function enclosing(stack, root, index) {
73236
+ let top = stack.at(-1);
73237
+ while (top && stack.length > 1 && index >= top.end) {
73238
+ stack.pop();
73239
+ top = stack.at(-1);
73240
+ }
73241
+ return top?.node ?? root;
73242
+ }
73243
+ function buildTree(ctx) {
73244
+ const { rootTag } = ctx;
73245
+ const rootEnd = rootTag?.closeIndex;
73246
+ if (!rootTag || rootEnd == null) return null;
73247
+ const root = toNode(rootTag);
73248
+ const stack = [{ node: root, end: rootEnd }];
73249
+ for (const tag of ctx.tags.filter((t) => t.index > rootTag.index && t.index < rootEnd)) {
73250
+ const node = toNode(tag);
73251
+ enclosing(stack, root, tag.index).children.push(node);
73252
+ if (tag.closeIndex != null) stack.push({ node, end: tag.closeIndex });
73253
+ }
73254
+ return root;
73255
+ }
73256
+ function hasNestedStructure(node) {
73257
+ if (OPAQUE_TAGS.has(node.tag)) return null;
73258
+ for (const child of node.children) {
73259
+ if (NON_LAYOUT_TAGS.has(child.tag)) continue;
73260
+ if (!INLINE_TEXT_TAGS.has(child.tag)) return child;
73261
+ const deeper = hasNestedStructure(child);
73262
+ if (deeper) return deeper;
73263
+ }
73264
+ return null;
73265
+ }
73266
+ function nestedStructureFindings(rows, severity) {
73267
+ return rows.flatMap((row) => {
73268
+ const nested = isSubCompositionHost(row) ? null : hasNestedStructure(row);
73269
+ if (!nested) return [];
73270
+ return [
73271
+ {
73272
+ code: "nested_structure_needs_subcomposition",
73273
+ severity,
73274
+ message: `${describe(row)} is a timeline element that contains nested ${describe(nested)}. The timeline shows one row per top-level element, and the root composition is built only from sub-compositions.`,
73275
+ elementId: row.attrs.id,
73276
+ fixHint: `Move ${describe(row)} and its contents into a sub-composition file and mount it with data-composition-src.`
73277
+ }
73278
+ ];
73279
+ });
73280
+ }
73281
+ function missingDurationFindings(rows, severity) {
73282
+ const intendedClip = (row) => !MEDIA_TAGS2.has(row.tag) || row.attrs["data-start"] !== void 0 || row.attrs["data-track-index"] !== void 0;
73283
+ return rows.filter(
73284
+ (row) => row.attrs["data-duration"] === void 0 && row.attrs["data-end"] === void 0 && !isSubCompositionHost(row) && intendedClip(row)
73285
+ ).map((row) => {
73286
+ const isMedia = MEDIA_TAGS2.has(row.tag);
73287
+ return {
73288
+ code: isMedia ? "media_missing_duration" : "timeline_element_missing_timing",
73289
+ severity,
73290
+ message: isMedia ? `${describe(row)} is media on the timeline without data-duration, so its clip has no length.` : `${describe(row)} is a timeline element without data-duration, so the timeline cannot draw where it ends.`,
73291
+ elementId: row.attrs.id,
73292
+ fixHint: `Add data-duration (in seconds) to ${describe(row)}.`
73293
+ };
73294
+ });
73295
+ }
73296
+ function captionFindings(rows, severity) {
73297
+ const captionRows = rows.filter((row) => trackKindOf(row).kind === "captions");
73298
+ const findings = captionRows.filter((row) => trackKindOf(row).source === "legacy-captions").map((row) => ({
73299
+ code: "caption_track_kind_missing",
73300
+ severity,
73301
+ message: `${describe(row)} looks like captions but is not marked as such, so the timeline cannot group it on the caption track.`,
73302
+ elementId: row.attrs.id,
73303
+ fixHint: `Add data-track-kind="captions" to ${describe(row)}.`
73304
+ }));
73305
+ const lanes = new Set(
73306
+ captionRows.flatMap((row) => row.attrs["data-track-index"] ?? row.attrs["data-track"] ?? [])
73307
+ );
73308
+ if (lanes.size > 1) {
73309
+ findings.push({
73310
+ code: "multiple_caption_tracks",
73311
+ severity,
73312
+ message: `Captions are spread across ${lanes.size} timeline tracks. Keep all captions on one track.`,
73313
+ fixHint: "Give every caption element the same data-track-index."
73314
+ });
73315
+ }
73316
+ return findings;
73317
+ }
73084
73318
  function dedupeKeyFor(finding) {
73085
73319
  return [
73086
73320
  finding.code,
@@ -73135,7 +73369,7 @@ async function lintHyperframeHtml(html, options = {}) {
73135
73369
  timings: { totalMs: performance.now() - startedAt, ...timings }
73136
73370
  };
73137
73371
  }
73138
- var import_postcss_selector_parser, COMPOSITION_ID_IN_CSS_PATTERN, TIMELINE_REGISTRY_INIT_PATTERN, TIMELINE_REGISTRY_OBJECT_LITERAL_PATTERN, TIMELINE_REGISTRY_ASSIGN_PATTERN, WINDOW_TIMELINE_ASSIGN_PATTERN, INVALID_SCRIPT_CLOSE_PATTERN, TIMELINE_REGISTRY_KEY_PATTERN, TIMELINE_REGISTRY_OBJECT_OPEN_PATTERN, TIMELINE_REGISTRY_OBJECT_ENTRY_PATTERN, REGEX_ALLOWED_BEFORE, REGEX_ALLOWED_KEYWORDS, WORD_CHAR, CodeContext, VISIBLE_MARKUP_COMMENT_PATTERN, VISIBLE_MARKUP_COMMENT_PROTECTED_BLOCK_PATTERN, coreRules, IMAGE_SRC_EXT, VIDEO_SRC_EXT, AUDIO_SRC_EXT, SRC_KIND_NOUN, mediaRules, AUDIO_GROUP_TIMING_ATTRS, MAX_COMPOSITION_LINES, MAX_TIMED_ELEMENTS_PER_TRACK, TRACK_DENSITY_EXEMPT_TAGS, CAPTION_CUE_TOKEN, HEAVY_OVERLAY_ELEMENT_COUNT_WARN, HEAVY_OVERLAY_EXEMPT_TAGS, HEAVY_OVERLAY_CSS_PATTERN, INLINE_STYLE_DISPLAY_NONE_PATTERN, compositionRules, SCENE_BOUNDARY_EPSILON_SECONDS, UNRESOLVED_TARGET, gsapWindowsCache, CONFLICTING_TRANSLATE_PROPS, CONFLICTING_SCALE_PROPS, RELATIVE_TWEEN_VALUE, TRANSFORM_SENSITIVE_READ, TRANSFORM_INVARIANT_READ, CALLBACK_MEASUREMENT_PATTERN, NUMBER_METHODS, gsapRules, captionRules, adapterRules, TEXTURE_BASE_CLASS, TEXTURE_CLASS_PREFIX, textureRules, GENERIC_FAMILIES, fontRules, slideshowRules, RULE_GROUPS, LINT_RULE_COUNT, LINT_RULE_GROUP_COUNTS;
73372
+ var import_postcss_selector_parser, COMPOSITION_ID_IN_CSS_PATTERN, TIMELINE_REGISTRY_INIT_PATTERN, TIMELINE_REGISTRY_OBJECT_LITERAL_PATTERN, TIMELINE_REGISTRY_ASSIGN_PATTERN, WINDOW_TIMELINE_ASSIGN_PATTERN, INVALID_SCRIPT_CLOSE_PATTERN, TIMELINE_REGISTRY_KEY_PATTERN, TIMELINE_REGISTRY_OBJECT_OPEN_PATTERN, TIMELINE_REGISTRY_OBJECT_ENTRY_PATTERN, REGEX_ALLOWED_BEFORE, REGEX_ALLOWED_KEYWORDS, WORD_CHAR, CodeContext, VISIBLE_MARKUP_COMMENT_PATTERN, VISIBLE_MARKUP_COMMENT_PROTECTED_BLOCK_PATTERN, coreRules, IMAGE_SRC_EXT, VIDEO_SRC_EXT, AUDIO_SRC_EXT, SRC_KIND_NOUN, mediaRules, AUDIO_GROUP_TIMING_ATTRS, MAX_COMPOSITION_LINES, MAX_TIMED_ELEMENTS_PER_TRACK, TRACK_DENSITY_EXEMPT_TAGS, CAPTION_CUE_TOKEN, HEAVY_OVERLAY_ELEMENT_COUNT_WARN, HEAVY_OVERLAY_EXEMPT_TAGS, HEAVY_OVERLAY_CSS_PATTERN, INLINE_STYLE_DISPLAY_NONE_PATTERN, compositionRules, SCENE_BOUNDARY_EPSILON_SECONDS, UNRESOLVED_TARGET, gsapWindowsCache, CONFLICTING_TRANSLATE_PROPS, CONFLICTING_SCALE_PROPS, RELATIVE_TWEEN_VALUE, TRANSFORM_SENSITIVE_READ, TRANSFORM_INVARIANT_READ, CALLBACK_MEASUREMENT_PATTERN, NUMBER_METHODS, gsapRules, captionRules, adapterRules, TEXTURE_BASE_CLASS, TEXTURE_CLASS_PREFIX, textureRules, GENERIC_FAMILIES, fontRules, slideshowRules, INLINE_TEXT_TAGS, OPAQUE_TAGS, NON_LAYOUT_TAGS, MEDIA_TAGS2, NODE_ATTRS, describe, structureRules, RULE_GROUPS, LINT_RULE_COUNT, LINT_RULE_GROUP_COUNTS;
73139
73373
  var init_dist3 = __esm({
73140
73374
  "../lint/dist/index.js"() {
73141
73375
  "use strict";
@@ -73149,6 +73383,7 @@ var init_dist3 = __esm({
73149
73383
  init_compositionContract();
73150
73384
  init_composition();
73151
73385
  init_slideshow();
73386
+ init_topLevelElements();
73152
73387
  init_esm10();
73153
73388
  init_esm10();
73154
73389
  COMPOSITION_ID_IN_CSS_PATTERN = /\[data-composition-id=["']([^"']+)["']\]/g;
@@ -76224,6 +76459,64 @@ ${other.raw}`)
76224
76459
  return findings;
76225
76460
  }
76226
76461
  ];
76462
+ INLINE_TEXT_TAGS = /* @__PURE__ */ new Set([
76463
+ "br",
76464
+ "wbr",
76465
+ "b",
76466
+ "i",
76467
+ "u",
76468
+ "s",
76469
+ "em",
76470
+ "strong",
76471
+ "small",
76472
+ "sub",
76473
+ "sup",
76474
+ "mark",
76475
+ "span",
76476
+ "a",
76477
+ "code",
76478
+ "abbr"
76479
+ ]);
76480
+ OPAQUE_TAGS = /* @__PURE__ */ new Set([
76481
+ "svg",
76482
+ "canvas",
76483
+ "picture",
76484
+ "video",
76485
+ "audio",
76486
+ "select",
76487
+ "textarea",
76488
+ "iframe",
76489
+ "object"
76490
+ ]);
76491
+ NON_LAYOUT_TAGS = /* @__PURE__ */ new Set(["style", "script", "template", "noscript"]);
76492
+ MEDIA_TAGS2 = /* @__PURE__ */ new Set(["img", "video", "audio"]);
76493
+ NODE_ATTRS = [
76494
+ "id",
76495
+ "class",
76496
+ "data-start",
76497
+ "data-duration",
76498
+ "data-end",
76499
+ "data-track",
76500
+ "data-track-index",
76501
+ "data-track-kind",
76502
+ "data-composition-id",
76503
+ "data-composition-src"
76504
+ ];
76505
+ describe = (node) => `<${node.tag}${node.attrs.id ? ` id="${node.attrs.id}"` : ""}>`;
76506
+ structureRules = [
76507
+ (ctx) => {
76508
+ if (ctx.options.isSubComposition) return [];
76509
+ const root = buildTree(ctx);
76510
+ if (!root) return [];
76511
+ const severity = ctx.options.host === "studio" ? "error" : "warning";
76512
+ const rows = topLevelElements(root);
76513
+ return [
76514
+ ...nestedStructureFindings(rows, severity),
76515
+ ...missingDurationFindings(rows, severity),
76516
+ ...captionFindings(rows, severity)
76517
+ ];
76518
+ }
76519
+ ];
76227
76520
  RULE_GROUPS = [
76228
76521
  { group: "core", rules: coreRules },
76229
76522
  { group: "media", rules: mediaRules },
@@ -76233,7 +76526,8 @@ ${other.raw}`)
76233
76526
  { group: "adapters", rules: adapterRules },
76234
76527
  { group: "textures", rules: textureRules },
76235
76528
  { group: "fonts", rules: fontRules },
76236
- { group: "slideshow", rules: slideshowRules }
76529
+ { group: "slideshow", rules: slideshowRules },
76530
+ { group: "structure", rules: structureRules }
76237
76531
  ];
76238
76532
  LINT_RULE_COUNT = RULE_GROUPS.reduce((n, g) => n + g.rules.length, 0);
76239
76533
  LINT_RULE_GROUP_COUNTS = Object.fromEntries(
@@ -76675,6 +76969,7 @@ var MAX_INLINE_ASSET_BYTES;
76675
76969
  var init_htmlBundler = __esm({
76676
76970
  "../core/dist/compiler/htmlBundler.js"() {
76677
76971
  "use strict";
76972
+ init_scriptRuns();
76678
76973
  init_externalScripts();
76679
76974
  init_flattenedRoot();
76680
76975
  init_flattenedRoot();
@@ -76715,6 +77010,7 @@ var init_compiler = __esm({
76715
77010
  init_getVariables();
76716
77011
  init_htmlParityContract();
76717
77012
  init_htmlDocument();
77013
+ init_scriptRuns();
76718
77014
  init_staticGuard();
76719
77015
  init_compositionScoping();
76720
77016
  init_inlineSubCompositions();
@@ -82515,6 +82811,7 @@ __export(src_exports, {
82515
82811
  TRANSITIONS: () => TRANSITIONS,
82516
82812
  VIDEO_FRAME_FORMATS: () => VIDEO_FRAME_FORMATS,
82517
82813
  VideoSourceExtractionError: () => VideoSourceExtractionError,
82814
+ WebGpuUnavailableError: () => WebGpuUnavailableError,
82518
82815
  acquireBrowser: () => acquireBrowser,
82519
82816
  analyzeClipMediaFit: () => analyzeClipMediaFit,
82520
82817
  analyzeCompositionHdr: () => analyzeCompositionHdr,
@@ -82527,6 +82824,7 @@ __export(src_exports, {
82527
82824
  assertMediaPayload: () => assertMediaPayload,
82528
82825
  assertPublicHttpsUrl: () => assertPublicHttpsUrl,
82529
82826
  assertSwiftShader: () => assertSwiftShader,
82827
+ assertWebGpuAdapterAvailable: () => assertWebGpuAdapterAvailable,
82530
82828
  augmentPageNavigationTimeoutError: () => augmentPageNavigationTimeoutError,
82531
82829
  augmentProtocolTimeoutError: () => augmentProtocolTimeoutError,
82532
82830
  beginFrameCapture: () => beginFrameCapture,
@@ -82550,6 +82848,7 @@ __export(src_exports, {
82550
82848
  cloneCaptureWarnings: () => cloneCaptureWarnings,
82551
82849
  closeCaptureSession: () => closeCaptureSession,
82552
82850
  completeDeferredDrawElementInit: () => completeDeferredDrawElementInit,
82851
+ compositionRequiresWebGpu: () => compositionRequiresWebGpu,
82553
82852
  computeWorkerSizing: () => computeWorkerSizing,
82554
82853
  convertTransfer: () => convertTransfer,
82555
82854
  createCaptureSession: () => createCaptureSession,
@@ -89771,6 +90070,37 @@ async function ensureWoff2DataUri(cachePath, woff2Url, familyName, weight, style
89771
90070
  }
89772
90071
  return `data:font/woff2;base64,${readFileSync9(cachePath).toString("base64")}`;
89773
90072
  }
90073
+ var googleFontCssCache = /* @__PURE__ */ new Map();
90074
+ function raceAgainstAbort(promise, signal) {
90075
+ if (!signal) return promise;
90076
+ if (signal.aborted) return Promise.reject(callerAbortReason(signal));
90077
+ return new Promise((resolve18, reject) => {
90078
+ const onAbort = () => reject(callerAbortReason(signal));
90079
+ signal.addEventListener("abort", onAbort, { once: true });
90080
+ promise.then(resolve18, reject).finally(() => signal.removeEventListener("abort", onAbort));
90081
+ });
90082
+ }
90083
+ function fetchGoogleFontCss(url, familyName, options) {
90084
+ const key2 = `${options.failClosedFontFetch ? "closed" : "open"}:${url}`;
90085
+ let shared = googleFontCssCache.get(key2);
90086
+ if (!shared) {
90087
+ shared = fetchFontResource(
90088
+ url,
90089
+ { headers: { "User-Agent": WOFF2_USER_AGENT } },
90090
+ (response) => response.text(),
90091
+ familyName,
90092
+ "Google Fonts CSS",
90093
+ { ...options, abortSignal: void 0 }
90094
+ ).then((result) => {
90095
+ if (result.ok) return { ok: true, body: result.body };
90096
+ if (isRetryableFontFetchStatus(result.response.status)) googleFontCssCache.delete(key2);
90097
+ return { ok: false };
90098
+ });
90099
+ googleFontCssCache.set(key2, shared);
90100
+ shared.catch(() => googleFontCssCache.delete(key2));
90101
+ }
90102
+ return raceAgainstAbort(shared, options.abortSignal);
90103
+ }
89774
90104
  async function fetchGoogleFont(familyName, options, fontText) {
89775
90105
  const slug = fontSlug(familyName);
89776
90106
  const googleFamilyName = familyName.replace(/\+/g, " ");
@@ -89779,14 +90109,7 @@ async function fetchGoogleFont(familyName, options, fontText) {
89779
90109
  const url = `https://fonts.googleapis.com/css2?family=${encodedFamily}:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,400;1,700${textParam}`;
89780
90110
  let cssText;
89781
90111
  try {
89782
- const cssResult = await fetchFontResource(
89783
- url,
89784
- { headers: { "User-Agent": WOFF2_USER_AGENT } },
89785
- (response) => response.text(),
89786
- familyName,
89787
- "Google Fonts CSS",
89788
- options
89789
- );
90112
+ const cssResult = await fetchGoogleFontCss(url, familyName, options);
89790
90113
  if (!cssResult.ok) {
89791
90114
  return [];
89792
90115
  }
@@ -91234,22 +91557,16 @@ function coalesceHeadStylesAndBodyScripts(html) {
91234
91557
  }
91235
91558
  }
91236
91559
  if (body) {
91237
- const bodyScripts = Array.from(body.querySelectorAll("script")).filter((el) => {
91238
- const src = (el.getAttribute("src") || "").trim();
91239
- if (src) return false;
91240
- const type = (el.getAttribute("type") || "").trim().toLowerCase();
91241
- return !type || type === "text/javascript" || type === "application/javascript";
91242
- });
91243
- if (bodyScripts.length > 0) {
91244
- const mergedJs = bodyScripts.map((el) => (el.textContent || "").trim()).filter(Boolean).join("\n;\n").trim();
91245
- for (const el of bodyScripts) {
91246
- el.remove();
91247
- }
91248
- if (mergedJs) {
91249
- const script = document2.createElement("script");
91250
- script.textContent = mergedJs;
91251
- body.appendChild(script);
91252
- }
91560
+ for (const { members, anchor } of inlineScriptRuns(
91561
+ Array.from(body.querySelectorAll("script"))
91562
+ )) {
91563
+ const mergedJs = members.map((el) => (el.textContent || "").trim()).filter(Boolean).join("\n;\n").trim();
91564
+ for (const el of members) el.remove();
91565
+ if (!mergedJs) continue;
91566
+ const script = document2.createElement("script");
91567
+ script.textContent = mergedJs;
91568
+ if (anchor) anchor.before(script);
91569
+ else body.appendChild(script);
91253
91570
  }
91254
91571
  }
91255
91572
  return document2.toString();
@@ -92612,7 +92929,8 @@ async function runProbeStage(input) {
92612
92929
  quality: captureFormat === "png" ? void 0 : 80,
92613
92930
  variables: job.config.variables,
92614
92931
  deviceScaleFactor,
92615
- motionBlur
92932
+ motionBlur,
92933
+ requiresWebGpu: compositionRequiresWebGpu(compiled.html)
92616
92934
  };
92617
92935
  const PROBE_MAX_ATTEMPTS = 2;
92618
92936
  for (let attempt = 1; attempt <= PROBE_MAX_ATTEMPTS; attempt++) {
@@ -98303,7 +98621,8 @@ async function executeRenderPipeline(input) {
98303
98621
  skipReadinessVideoIds: videoReadinessSkipIds,
98304
98622
  // Probe-resolved duration: drawElement self-verification derives its
98305
98623
  // sample frame indices from this so they land inside the drained range.
98306
- compositionDurationSeconds: job.duration
98624
+ compositionDurationSeconds: job.duration,
98625
+ requiresWebGpu: compositionRequiresWebGpu(compiled.html)
98307
98626
  });
98308
98627
  const createRenderVideoFrameInjector = () => createVideoFrameInjector(frameLookup, {
98309
98628
  frameDataUriCacheLimit: cfg.frameDataUriCacheLimit,
@@ -100654,8 +100973,8 @@ async function runFontLocalize(io, localize) {
100654
100973
  }
100655
100974
 
100656
100975
  // src/version.ts
100657
- var VERSION = true ? "0.8.48" : "0.0.0-dev";
100658
- var PRODUCER_VERSION = true ? "0.8.48" : "0.0.0-dev";
100976
+ var VERSION = true ? "0.8.50" : "0.0.0-dev";
100977
+ var PRODUCER_VERSION = true ? "0.8.50" : "0.0.0-dev";
100659
100978
 
100660
100979
  // src/fontLocalizeCli.ts
100661
100980
  async function readStdin() {