@hyperframes/producer 0.8.203 → 0.8.204

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.
@@ -1335,6 +1335,12 @@ import {
1335
1335
  prepareCaptureSessionForReuse,
1336
1336
  spawnStreamingEncoder
1337
1337
  } from "@hyperframes/engine";
1338
+ var DEFAULT_CAPTURE_STALL_MS = 6e4;
1339
+ function resolveCaptureStallTimeoutMs() {
1340
+ const raw = process.env.HF_DE_STALL_MS ?? process.env.HF_DE_PARALLEL_STALL_MS;
1341
+ const parsed = raw ? Number(raw) : Number.NaN;
1342
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_CAPTURE_STALL_MS;
1343
+ }
1338
1344
 
1339
1345
  // src/services/render/segmentManifest.ts
1340
1346
  import { renameWithRetry as renameWithRetry2 } from "@hyperframes/engine";
@@ -1442,9 +1448,325 @@ async function measureCaptureCostFromSession(session, totalFrames, fps, log) {
1442
1448
 
1443
1449
  // src/services/render/observability.ts
1444
1450
  import { redactTelemetryString } from "@hyperframes/core";
1451
+ import { CaptureFailure as CaptureFailure2 } from "@hyperframes/engine";
1452
+ var MAX_EVENTS = 160;
1453
+ var ALLOWED_STRING_DATA_KEYS = /* @__PURE__ */ new Set([
1454
+ "browserGpuMode",
1455
+ "captureMode",
1456
+ "captureOperation",
1457
+ "compositionHash",
1458
+ "effectiveHdr",
1459
+ "format",
1460
+ "quality",
1461
+ "renderJobId",
1462
+ "requestedHdrMode",
1463
+ "requestedWorkers",
1464
+ // "calibrating" during pre-capture browser warm-up / calibration stages;
1465
+ // "capturing" during capture_disk / capture_streaming / capture_hdr_*
1466
+ // stages. Distinguishes healthy 0-frame heartbeats (browser starting up)
1467
+ // from actual zero-frame stalls once capture is meant to be underway.
1468
+ // Field signal ts=1784019503.
1469
+ "stagePhase",
1470
+ // Full-fidelity fast-capture fallback trigger — the specific reason
1471
+ // drawElement gated off ("filter:blur", "filter:drop-shadow",
1472
+ // "backdrop-filter", "clip-path", "at_risk_timeline", "swiftshader", …).
1473
+ // Populated on the `capture_fallback_profile` checkpoint emitted by
1474
+ // `fallbackCaptureProfile.ts` when the operator opts into
1475
+ // `HF_PROFILE_FALLBACK_CAPTURE=true` so downstream metric consumers can
1476
+ // characterize per-frame perf by the exact trigger. Complementary to
1477
+ // `deGateReason`, which is the low-cardinality bucket for aggregation —
1478
+ // this string preserves the fine-grained "which CSS property" for
1479
+ // diagnostic reads. Sanitized like any observation message; low-cardinality
1480
+ // by construction (bounded by the fallback-trigger enumeration).
1481
+ "triggerReason"
1482
+ ]);
1483
+ var RESERVED_LOG_KEYS = /* @__PURE__ */ new Set([
1484
+ "data",
1485
+ "durationMs",
1486
+ "elapsedMs",
1487
+ "message",
1488
+ "phase",
1489
+ "renderJobId",
1490
+ "status"
1491
+ ]);
1492
+ function sanitizeObservationMessage(value) {
1493
+ return redactTelemetryString(value);
1494
+ }
1495
+ function sanitizeObservationData(data) {
1496
+ if (!data) return void 0;
1497
+ const sanitized = {};
1498
+ for (const [key, value] of Object.entries(data)) {
1499
+ if (RESERVED_LOG_KEYS.has(key)) continue;
1500
+ if (typeof value === "string") {
1501
+ if (!ALLOWED_STRING_DATA_KEYS.has(key)) continue;
1502
+ sanitized[key] = sanitizeObservationMessage(value);
1503
+ } else {
1504
+ sanitized[key] = value;
1505
+ }
1506
+ }
1507
+ return sanitized;
1508
+ }
1509
+ function isHttpErrorDiagnostic(line) {
1510
+ return /\[Browser:HTTP\d{3}\]/.test(line);
1511
+ }
1512
+ function readUnsignedIntAfter(line, prefix) {
1513
+ const start = line.indexOf(prefix);
1514
+ if (start < 0) return void 0;
1515
+ let value = 0;
1516
+ let digits = 0;
1517
+ for (let i = start + prefix.length; i < line.length; i++) {
1518
+ const code = line.charCodeAt(i);
1519
+ if (code < 48 || code > 57) break;
1520
+ value = value * 10 + code - 48;
1521
+ digits += 1;
1522
+ if (value > Number.MAX_SAFE_INTEGER) return void 0;
1523
+ }
1524
+ return digits > 0 ? value : void 0;
1525
+ }
1526
+ function maxReading(current, next) {
1527
+ if (next === void 0) return current;
1528
+ return current === void 0 ? next : Math.max(current, next);
1529
+ }
1530
+ function summarizeInitObservability(lines, fallback) {
1531
+ let initDurationMs = fallback?.initDurationMs;
1532
+ let tweenCount = fallback?.tweenCount;
1533
+ let elementCount = fallback?.elementCount;
1534
+ for (const line of lines) {
1535
+ if (!line.includes("[FrameCapture:INIT]")) continue;
1536
+ initDurationMs = maxReading(initDurationMs, readUnsignedIntAfter(line, "initDurationMs="));
1537
+ tweenCount = maxReading(tweenCount, readUnsignedIntAfter(line, "tweenCount="));
1538
+ elementCount = maxReading(elementCount, readUnsignedIntAfter(line, "elementCount="));
1539
+ }
1540
+ if (initDurationMs === void 0 && tweenCount === void 0 && elementCount === void 0) {
1541
+ return void 0;
1542
+ }
1543
+ return { initDurationMs, tweenCount, elementCount };
1544
+ }
1545
+ function summarizeBrowserDiagnostics(lines) {
1546
+ let errors = 0;
1547
+ let pageErrors = 0;
1548
+ let requestFailed = 0;
1549
+ let httpErrors = 0;
1550
+ let navigationStarts = 0;
1551
+ let navigationFailures = 0;
1552
+ let consoleErrors = 0;
1553
+ let consoleWarnings = 0;
1554
+ for (const line of lines) {
1555
+ const isPageError = line.includes("PAGEERROR");
1556
+ const isRequestFailed = line.includes("REQUESTFAILED");
1557
+ const isHttpError = isHttpErrorDiagnostic(line);
1558
+ const isNavigationFailure = line.includes("[FrameCapture:ERROR] page.goto failed");
1559
+ const isConsoleError = line.includes("[error]");
1560
+ if (isPageError) pageErrors++;
1561
+ if (isRequestFailed) requestFailed++;
1562
+ if (isHttpError) httpErrors++;
1563
+ if (line.includes("[FrameCapture:NAV] page.goto start")) navigationStarts++;
1564
+ if (isNavigationFailure) navigationFailures++;
1565
+ if (isConsoleError) consoleErrors++;
1566
+ if (line.includes("[warn]")) consoleWarnings++;
1567
+ if (line.includes("ERROR") && !isPageError && !isRequestFailed && !isHttpError && !isNavigationFailure && !isConsoleError) {
1568
+ errors++;
1569
+ }
1570
+ }
1571
+ return {
1572
+ total: lines.length,
1573
+ errors,
1574
+ pageErrors,
1575
+ requestFailed,
1576
+ httpErrors,
1577
+ navigationStarts,
1578
+ navigationFailures,
1579
+ consoleErrors,
1580
+ consoleWarnings
1581
+ };
1582
+ }
1583
+ var RenderObservabilityRecorder = class {
1584
+ constructor(input) {
1585
+ this.input = input;
1586
+ }
1587
+ input;
1588
+ events = [];
1589
+ eventCount = 0;
1590
+ failedPhase;
1591
+ checkpoint(phase, message, data) {
1592
+ return this.record({
1593
+ phase,
1594
+ status: "checkpoint",
1595
+ elapsedMs: Date.now() - this.input.pipelineStartMs,
1596
+ message: sanitizeObservationMessage(message),
1597
+ data: sanitizeObservationData(data)
1598
+ });
1599
+ }
1600
+ stageStart(phase, data) {
1601
+ this.record({
1602
+ phase,
1603
+ status: "start",
1604
+ elapsedMs: Date.now() - this.input.pipelineStartMs,
1605
+ data: sanitizeObservationData(data)
1606
+ });
1607
+ return Date.now();
1608
+ }
1609
+ stageEnd(phase, startedAtMs, data) {
1610
+ this.record({
1611
+ phase,
1612
+ status: "end",
1613
+ elapsedMs: Date.now() - this.input.pipelineStartMs,
1614
+ durationMs: Date.now() - startedAtMs,
1615
+ data: sanitizeObservationData(data)
1616
+ });
1617
+ }
1618
+ stageError(phase, startedAtMs, error, data) {
1619
+ this.failedPhase = phase;
1620
+ this.record({
1621
+ phase,
1622
+ status: "error",
1623
+ elapsedMs: Date.now() - this.input.pipelineStartMs,
1624
+ durationMs: Date.now() - startedAtMs,
1625
+ message: sanitizeObservationMessage(normalizeErrorMessage(error)),
1626
+ data: sanitizeObservationData(data)
1627
+ });
1628
+ }
1629
+ summary(input) {
1630
+ const lastEvent = this.events[this.events.length - 1];
1631
+ return {
1632
+ renderJobId: this.input.renderJobId,
1633
+ compositionHash: input.compositionHash,
1634
+ events: this.events.slice(),
1635
+ eventCount: this.eventCount,
1636
+ lastEvent,
1637
+ failedPhase: this.failedPhase,
1638
+ browserDiagnostics: summarizeBrowserDiagnostics(input.lastBrowserConsole),
1639
+ capture: { ...input.capture },
1640
+ extraction: input.extraction ? { ...input.extraction } : void 0,
1641
+ init: summarizeInitObservability(input.lastBrowserConsole, input.initFallback)
1642
+ };
1643
+ }
1644
+ hasFailure() {
1645
+ return this.failedPhase !== void 0;
1646
+ }
1647
+ /** A phase failure that was subsequently recovered (e.g. the drawElement
1648
+ * self-verify fallback re-rendering via screenshot) should not brand the
1649
+ * whole render as failed in the summary. */
1650
+ clearFailure(phase) {
1651
+ if (this.failedPhase === phase) this.failedPhase = void 0;
1652
+ }
1653
+ record(event) {
1654
+ this.eventCount++;
1655
+ const eventWithJob = { ...event, renderJobId: this.input.renderJobId };
1656
+ this.events.push(eventWithJob);
1657
+ if (this.events.length > MAX_EVENTS) {
1658
+ this.events.shift();
1659
+ }
1660
+ this.input.log.info("[Render:trace]", {
1661
+ renderJobId: eventWithJob.renderJobId,
1662
+ phase: eventWithJob.phase,
1663
+ status: eventWithJob.status,
1664
+ elapsedMs: eventWithJob.elapsedMs,
1665
+ durationMs: eventWithJob.durationMs,
1666
+ message: eventWithJob.message,
1667
+ ...eventWithJob.data
1668
+ });
1669
+ return eventWithJob;
1670
+ }
1671
+ };
1445
1672
  var HEARTBEAT_RAMP_MS = [3e4, 6e4, 12e4];
1446
1673
  var HEARTBEAT_REPEAT_MS = 12e4;
1447
1674
  var HEARTBEAT_RAMP_END_MS = HEARTBEAT_RAMP_MS[HEARTBEAT_RAMP_MS.length - 1] ?? HEARTBEAT_REPEAT_MS;
1675
+ function heartbeatTargetMs(index) {
1676
+ const rampTarget = HEARTBEAT_RAMP_MS[index];
1677
+ if (rampTarget !== void 0) return rampTarget;
1678
+ const overflow = index - HEARTBEAT_RAMP_MS.length + 1;
1679
+ return HEARTBEAT_RAMP_END_MS + overflow * HEARTBEAT_REPEAT_MS;
1680
+ }
1681
+ var RenderStageStalledError = class extends CaptureFailure2 {
1682
+ code = "RENDER_STAGE_STALLED";
1683
+ owner = "system";
1684
+ retryable = false;
1685
+ constructor(phase, silentMs, progress) {
1686
+ super({
1687
+ kind: "protocol_timeout",
1688
+ message: `[Render] ${phase} stalled: no progress for ${silentMs}ms (stuck at ${progress}).`
1689
+ });
1690
+ this.name = "RenderStageStalledError";
1691
+ }
1692
+ };
1693
+ var STALL_POLL_MS = 5e3;
1694
+ function watchStall(phase, rule) {
1695
+ let last = rule.progress();
1696
+ let budgetMs = rule.firstProgressMs;
1697
+ let lastChangeAt = Date.now();
1698
+ let attempt;
1699
+ let timer;
1700
+ const restartWindow = (ms) => {
1701
+ last = rule.progress();
1702
+ budgetMs = ms;
1703
+ lastChangeAt = Date.now();
1704
+ };
1705
+ const tripped = new Promise((_, reject) => {
1706
+ timer = setInterval(
1707
+ () => {
1708
+ const current = rule.progress();
1709
+ if (current !== last) return restartWindow(rule.noProgressMs);
1710
+ if (Date.now() - lastChangeAt < budgetMs) return;
1711
+ const error = new RenderStageStalledError(phase, budgetMs, current);
1712
+ if (attempt && !attempt.signal.aborted) {
1713
+ attempt.abort(error);
1714
+ return restartWindow(rule.noProgressMs);
1715
+ }
1716
+ clearInterval(timer);
1717
+ reject(error);
1718
+ },
1719
+ Math.min(STALL_POLL_MS, rule.noProgressMs, rule.firstProgressMs)
1720
+ );
1721
+ });
1722
+ const openAttempt = (parent) => {
1723
+ attempt = new AbortController();
1724
+ restartWindow(rule.firstProgressMs);
1725
+ return parent ? AbortSignal.any([parent, attempt.signal]) : attempt.signal;
1726
+ };
1727
+ const stalledBy = () => attempt?.signal.aborted ? attempt.signal.reason : void 0;
1728
+ return { tripped, openAttempt, stalledBy, stop: () => clearInterval(timer) };
1729
+ }
1730
+ var openUnwatchedAttempt = (parent) => parent ?? new AbortController().signal;
1731
+ async function observeRenderStage(recorder, phase, data, fn, options = {}) {
1732
+ const heartbeatMessage = options.heartbeatMessage ?? "stage still running";
1733
+ const startedAt = recorder.stageStart(phase, data);
1734
+ let heartbeatCount = 0;
1735
+ let lastFiredAtMs = 0;
1736
+ let heartbeatTimer;
1737
+ const scheduleNextHeartbeat = () => {
1738
+ const targetMs = heartbeatTargetMs(heartbeatCount);
1739
+ heartbeatTimer = setTimeout(() => {
1740
+ lastFiredAtMs = targetMs;
1741
+ heartbeatCount += 1;
1742
+ recorder.checkpoint(phase, heartbeatMessage, {
1743
+ ...data,
1744
+ heartbeatIndex: heartbeatCount,
1745
+ stageElapsedMs: Date.now() - startedAt
1746
+ });
1747
+ scheduleNextHeartbeat();
1748
+ }, targetMs - lastFiredAtMs);
1749
+ heartbeatTimer.unref?.();
1750
+ };
1751
+ scheduleNextHeartbeat();
1752
+ const stall = options.stall ? watchStall(phase, options.stall) : void 0;
1753
+ const clearHeartbeats = () => {
1754
+ clearTimeout(heartbeatTimer);
1755
+ stall?.stop();
1756
+ };
1757
+ try {
1758
+ const run = fn(stall?.openAttempt ?? openUnwatchedAttempt);
1759
+ const result = await (stall ? Promise.race([run, stall.tripped]) : run);
1760
+ clearHeartbeats();
1761
+ recorder.stageEnd(phase, startedAt, data);
1762
+ return result;
1763
+ } catch (caught) {
1764
+ const error = stall?.stalledBy() ?? caught;
1765
+ clearHeartbeats();
1766
+ recorder.stageError(phase, startedAt, error, data);
1767
+ throw error;
1768
+ }
1769
+ }
1448
1770
 
1449
1771
  // src/services/render/videoFrameCoverage.ts
1450
1772
  import { parseHTML } from "linkedom";
@@ -3477,9 +3799,10 @@ async function discoverMediaFromBrowser(page) {
3477
3799
  const tagName = isImage ? "image" : htmlEl.tagName.toLowerCase() === "video" ? "video" : "audio";
3478
3800
  const id = htmlEl.getAttribute("data-hf-render-id") || htmlEl.id || (isImage ? autoImageIds.get(htmlEl) : void 0);
3479
3801
  if (!id) return;
3480
- const ownSrc = htmlEl.getAttribute("src");
3481
- const blankSrc = ownSrc !== null && ownSrc.trim() === "" && !htmlEl.querySelector("source[src]");
3482
- const src = blankSrc ? "" : htmlEl.currentSrc || htmlEl.src || ownSrc || "";
3802
+ const resolvedSrc = htmlEl.currentSrc || htmlEl.src || htmlEl.getAttribute("src") || "";
3803
+ const withoutQueryAndHash = (url) => url.replace(/[?#][\s\S]*$/, "");
3804
+ const isPageItself = withoutQueryAndHash(resolvedSrc) === withoutQueryAndHash(location.href);
3805
+ const src = isPageItself ? "" : resolvedSrc;
3483
3806
  const start = parseFloat(htmlEl.getAttribute("data-start") || "0");
3484
3807
  const endRaw = htmlEl.getAttribute("data-end");
3485
3808
  const durationRaw = htmlEl.getAttribute("data-duration");
@@ -5793,6 +6116,7 @@ async function runCaptureStage(input) {
5793
6116
  interleaveFrames: input.interleaveFrames,
5794
6117
  dedupPerfs,
5795
6118
  pageErrorLines: workerPageErrors,
6119
+ openAttempt: input.openAttempt,
5796
6120
  onProgress: (progress) => {
5797
6121
  if (progress.latestWorkerPhase) {
5798
6122
  if (progress.capturedFrames === 0) reportWorkerStartup(job, progress, onProgress);
@@ -5864,6 +6188,11 @@ async function runSequentialDiskCapture(input, captureOptions, captureCfg, check
5864
6188
  let transientRetriesUsed = 0;
5865
6189
  let resumeFrom = 0;
5866
6190
  while (true) {
6191
+ const attempt = input.openAttempt?.();
6192
+ const assertAttemptActive = () => {
6193
+ assertNotAborted();
6194
+ attempt?.throwIfAborted();
6195
+ };
5867
6196
  const videoInjector = createRenderVideoFrameInjector();
5868
6197
  const session = probeSession ?? await createCaptureSession5(
5869
6198
  fileServer.url,
@@ -5879,10 +6208,10 @@ async function runSequentialDiskCapture(input, captureOptions, captureCfg, check
5879
6208
  probeSession = null;
5880
6209
  }
5881
6210
  await ensureSessionReady(session);
5882
- assertNotAborted();
6211
+ assertAttemptActive();
5883
6212
  lastBrowserConsole = session.browserConsoleBuffer;
5884
6213
  await captureSessionFrames(session, {
5885
- input,
6214
+ input: { ...input, assertNotAborted: assertAttemptActive },
5886
6215
  rangeStart,
5887
6216
  rangeEnd,
5888
6217
  resumeFrom,
@@ -6566,7 +6895,17 @@ function isTransientCaptureRetryEligible(failureKind, missing, retriesUsed) {
6566
6895
  function captureAttemptMadeProgress(attemptTargetFrameCount, remainingFrameCount) {
6567
6896
  return remainingFrameCount < attemptTargetFrameCount;
6568
6897
  }
6898
+ var FIRST_FRAME_STALL_MS = 3e5;
6899
+ function frameStallRule(job) {
6900
+ const noProgressMs = resolveCaptureStallTimeoutMs();
6901
+ return {
6902
+ progress: () => job.framesRendered ?? 0,
6903
+ noProgressMs,
6904
+ firstProgressMs: Math.max(FIRST_FRAME_STALL_MS, noProgressMs)
6905
+ };
6906
+ }
6569
6907
  function isRecoverableParallelCaptureError(error) {
6908
+ if (error instanceof RenderStageStalledError) return true;
6570
6909
  const message = normalizeErrorMessage(error);
6571
6910
  if (!message.includes("[Parallel] Capture failed")) return false;
6572
6911
  const kind = classifyCaptureFailure3(error).kind;
@@ -6618,8 +6957,10 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
6618
6957
  )
6619
6958
  ];
6620
6959
  options.dedupPerfs.length = 0;
6960
+ let attemptSignal = options.abortSignal;
6621
6961
  try {
6622
6962
  for (const tasks of batches) {
6963
+ attemptSignal = options.openAttempt?.(options.abortSignal) ?? options.abortSignal;
6623
6964
  const capturedBeforeBatch = countCapturedFrames(
6624
6965
  options.totalFrames,
6625
6966
  options.framesDir,
@@ -6632,7 +6973,7 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
6632
6973
  tasks,
6633
6974
  options.captureOptions,
6634
6975
  options.createBeforeCaptureHook,
6635
- options.abortSignal,
6976
+ attemptSignal,
6636
6977
  options.onProgress ? (progress) => {
6637
6978
  options.onProgress?.({
6638
6979
  ...progress,
@@ -6680,7 +7021,8 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
6680
7021
  currentWorkers = nextWorkers;
6681
7022
  missingRanges = remaining;
6682
7023
  attempt++;
6683
- } catch (error) {
7024
+ } catch (caught) {
7025
+ const error = attemptSignal?.aborted && !options.abortSignal?.aborted ? attemptSignal.reason : caught;
6684
7026
  const failure = classifyCaptureFailure3(error, { signal: options.abortSignal });
6685
7027
  if (failure.kind === "cancelled") {
6686
7028
  throw error;
@@ -11091,29 +11433,36 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
11091
11433
  const captureSession = session;
11092
11434
  session = null;
11093
11435
  overlap.current = canEncodeWhileCapturing(encodeInput, chunkWorkerCount, cpus().length) ? encodeWhileCapturing(encodeInput, framesInChunk) : null;
11094
- await runCaptureStage({
11095
- fileServer,
11096
- workDir,
11097
- framesDir,
11098
- job,
11099
- totalFrames: framesInChunk,
11100
- cfg: captureCfg,
11101
- plan: capturePlan,
11102
- log,
11103
- probeSession: captureSession,
11104
- captureAttempts,
11105
- // This sink records each worker's effective capture mode so a
11106
- // fallback remains observable to adapters and smoke tests.
11107
- dedupPerfs: capturePerfs,
11108
- buildCaptureOptions: () => captureOptions,
11109
- createRenderVideoFrameInjector: createChunkVideoFrameInjector,
11110
- abortSignal: void 0,
11111
- assertNotAborted: () => {
11112
- },
11113
- frameRange: range,
11114
- onFrameLanded: (frame) => overlap.current?.onFrameLanded(frame),
11115
- interleaveFrames: interleavesCapture(overlap.current !== null, options)
11116
- }).catch(async (captureError) => {
11436
+ await observeRenderStage(
11437
+ new RenderObservabilityRecorder({ pipelineStartMs: start, log }),
11438
+ "capture_chunk",
11439
+ { chunkIndex },
11440
+ (openAttempt) => runCaptureStage({
11441
+ fileServer,
11442
+ workDir,
11443
+ framesDir,
11444
+ job,
11445
+ totalFrames: framesInChunk,
11446
+ cfg: captureCfg,
11447
+ plan: capturePlan,
11448
+ log,
11449
+ probeSession: captureSession,
11450
+ captureAttempts,
11451
+ // This sink records each worker's effective capture mode so a
11452
+ // fallback remains observable to adapters and smoke tests.
11453
+ dedupPerfs: capturePerfs,
11454
+ buildCaptureOptions: () => captureOptions,
11455
+ createRenderVideoFrameInjector: createChunkVideoFrameInjector,
11456
+ abortSignal: void 0,
11457
+ assertNotAborted: () => {
11458
+ },
11459
+ frameRange: range,
11460
+ onFrameLanded: (frame) => overlap.current?.onFrameLanded(frame),
11461
+ interleaveFrames: interleavesCapture(overlap.current !== null, options),
11462
+ openAttempt
11463
+ }),
11464
+ { stall: frameStallRule(job) }
11465
+ ).catch(async (captureError) => {
11117
11466
  await overlap.current?.abort();
11118
11467
  overlap.current = null;
11119
11468
  throw captureError;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": "0.1.0",
3
3
  "buildId": "dev",
4
- "sha256": "93402c78fff7cdcf247d971840896d2b0d3380fa1d0901f31618ba6f62160667",
4
+ "sha256": "7d7e26ef5f6c62ee5fd433020813c7253231a9ddaf89a3bcde67fd2d0ed33c66",
5
5
  "artifacts": {
6
6
  "iife": "hyperframe.runtime.iife.js",
7
7
  "esm": "hyperframe.runtime.mjs"