@hyperframes/engine 0.8.142 → 0.8.144
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 +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/services/browserManager.d.ts.map +1 -1
- package/dist/services/browserManager.js +53 -34
- package/dist/services/browserManager.js.map +1 -1
- package/dist/services/extractedFrameIndex.d.ts +1 -1
- package/dist/services/extractedFrameIndex.d.ts.map +1 -1
- package/dist/services/extractedFrameIndex.js +3 -1
- package/dist/services/extractedFrameIndex.js.map +1 -1
- package/dist/services/frameCapture.d.ts +13 -2
- package/dist/services/frameCapture.d.ts.map +1 -1
- package/dist/services/frameCapture.js +55 -46
- package/dist/services/frameCapture.js.map +1 -1
- package/dist/services/videoFrameExtractor.d.ts +18 -0
- package/dist/services/videoFrameExtractor.d.ts.map +1 -1
- package/dist/services/videoFrameExtractor.js +159 -6
- package/dist/services/videoFrameExtractor.js.map +1 -1
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/alphaBlit.js +4 -4
- package/dist/utils/alphaBlit.js.map +1 -1
- package/dist/utils/ffprobe.d.ts.map +1 -1
- package/dist/utils/ffprobe.js +101 -74
- package/dist/utils/ffprobe.js.map +1 -1
- package/package.json +3 -3
|
@@ -971,6 +971,7 @@ async function constructCaptureSession(input) {
|
|
|
971
971
|
browserConsoleBuffer: [],
|
|
972
972
|
scriptLoadFailures: [],
|
|
973
973
|
pageErrors: [],
|
|
974
|
+
subTimelineWaitMemo: sessionOptions.subTimelineWaitMemo ?? {},
|
|
974
975
|
warnings: [],
|
|
975
976
|
capturePerf: {
|
|
976
977
|
frames: 0,
|
|
@@ -1166,19 +1167,44 @@ async function pollHfReady(page, timeoutMs, intervalMs = 100) {
|
|
|
1166
1167
|
` State: __hf=${diag.hasHf}, seek=${diag.hasSeek}, player=${diag.hasPlayer}, ` +
|
|
1167
1168
|
`renderReady=${diag.renderReady}, duration=${diag.duration}`);
|
|
1168
1169
|
}
|
|
1169
|
-
|
|
1170
|
-
//
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1170
|
+
// Enumerated regardless of bail reason, so the warning names every composition still
|
|
1171
|
+
// waited on, not only a failed script URL.
|
|
1172
|
+
async function listUnregisteredTimelineIds(page) {
|
|
1173
|
+
const evaluated = await page.evaluate(`(function() {
|
|
1174
|
+
var hosts = document.querySelectorAll("[data-composition-id]");
|
|
1175
|
+
var timelines = window.__timelines || {};
|
|
1176
|
+
var m = [];
|
|
1177
|
+
for (var i = 0; i < hosts.length; i++) {
|
|
1178
|
+
if (hosts[i].hasAttribute("data-no-timeline")) continue;
|
|
1179
|
+
var id = hosts[i].getAttribute("data-composition-id");
|
|
1180
|
+
if (id && !timelines[id]) m.push(id);
|
|
1181
|
+
}
|
|
1182
|
+
return m;
|
|
1183
|
+
})()`);
|
|
1184
|
+
// Builds a warning, so it must never throw: normalise a loosely typed result.
|
|
1185
|
+
return Array.isArray(evaluated) ? evaluated.map((id) => String(id)) : [];
|
|
1186
|
+
}
|
|
1187
|
+
export async function waitForSubCompositionTimelines(session, page, timeoutMs) {
|
|
1188
|
+
const memo = session.subTimelineWaitMemo;
|
|
1189
|
+
session.subTimelineWaitOutcome = await pollSubCompositionTimelines(page, timeoutMs, {
|
|
1190
|
+
getScriptLoadFailures: () => session.scriptLoadFailures,
|
|
1191
|
+
onPending: (ids) => {
|
|
1192
|
+
session.pendingTimelineIds = [...ids];
|
|
1193
|
+
},
|
|
1194
|
+
shouldStop: () => session.vfxFailure !== undefined,
|
|
1195
|
+
knownUnregisteredIds: memo.unregisteredIds,
|
|
1196
|
+
});
|
|
1197
|
+
// Only a clean full-length timeout is replayed: a VFX stop ends early, and page or script
|
|
1198
|
+
// errors must reach each session so it can still classify the wait as a script failure.
|
|
1199
|
+
const cleanTimeout = session.subTimelineWaitOutcome === "timeout" &&
|
|
1200
|
+
session.vfxFailure === undefined &&
|
|
1201
|
+
session.pageErrors.length === 0 &&
|
|
1202
|
+
session.scriptLoadFailures.length === 0;
|
|
1203
|
+
if (cleanTimeout) {
|
|
1204
|
+
memo.unregisteredIds = session.pendingTimelineIds ?? [];
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
export async function pollSubCompositionTimelines(page, timeoutMs, { intervalMs = 150, getScriptLoadFailures, scriptFailureGraceMs = 2_000, onPending, shouldStop, knownUnregisteredIds = [], } = {}) {
|
|
1182
1208
|
// Hosts may opt out of the timeline wait with `data-no-timeline` —
|
|
1183
1209
|
// compositions driven purely by CSS animations / rAF (the render-compat
|
|
1184
1210
|
// contract) never register window.__timelines[id], and without the opt-out
|
|
@@ -1187,10 +1213,11 @@ shouldStop) {
|
|
|
1187
1213
|
var hosts = document.querySelectorAll("[data-composition-id]");
|
|
1188
1214
|
if (hosts.length === 0) return true;
|
|
1189
1215
|
var timelines = window.__timelines || {};
|
|
1216
|
+
var known = ${JSON.stringify(knownUnregisteredIds)};
|
|
1190
1217
|
for (var i = 0; i < hosts.length; i++) {
|
|
1191
1218
|
if (hosts[i].hasAttribute("data-no-timeline")) continue;
|
|
1192
1219
|
var id = hosts[i].getAttribute("data-composition-id");
|
|
1193
|
-
if (!id) continue;
|
|
1220
|
+
if (!id || known.indexOf(id) !== -1) continue;
|
|
1194
1221
|
if (!timelines[id]) return false;
|
|
1195
1222
|
}
|
|
1196
1223
|
return true;
|
|
@@ -1217,6 +1244,11 @@ shouldStop) {
|
|
|
1217
1244
|
}
|
|
1218
1245
|
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
1219
1246
|
}
|
|
1247
|
+
// Skipped known ids still count: if one is still unregistered, report the timeout again.
|
|
1248
|
+
const pendingIds = ready && knownUnregisteredIds.length === 0 ? [] : await listUnregisteredTimelineIds(page);
|
|
1249
|
+
if (ready && pendingIds.length > 0 && (getScriptLoadFailures?.() ?? []).length > 0) {
|
|
1250
|
+
scriptFailureBail = true;
|
|
1251
|
+
}
|
|
1220
1252
|
// Always force a timeline rebind once sub-composition timelines are
|
|
1221
1253
|
// confirmed present. The previous implementation only called rebind
|
|
1222
1254
|
// when the timeline count grew during the poll, which missed the case
|
|
@@ -1224,7 +1256,7 @@ shouldStop) {
|
|
|
1224
1256
|
// started — leaving child timelines un-nested in the root and causing
|
|
1225
1257
|
// the earliest sub-composition (data-start near 0) to render without
|
|
1226
1258
|
// its GSAP animations.
|
|
1227
|
-
if (ready) {
|
|
1259
|
+
if (ready && pendingIds.length === 0) {
|
|
1228
1260
|
await page.evaluate(`(function() {
|
|
1229
1261
|
if (typeof window.__hfForceTimelineRebind === "function") {
|
|
1230
1262
|
window.__hfForceTimelineRebind();
|
|
@@ -1232,26 +1264,6 @@ shouldStop) {
|
|
|
1232
1264
|
})()`);
|
|
1233
1265
|
return "ready";
|
|
1234
1266
|
}
|
|
1235
|
-
// Enumerate the still-unregistered composition ids regardless of bail
|
|
1236
|
-
// reason — a script-failure bail used to skip this entirely, so a render
|
|
1237
|
-
// with multiple sub-compositions only named the failed script URL(s), not
|
|
1238
|
-
// which composition(s) it was still waiting on (review).
|
|
1239
|
-
const evaluated = await page.evaluate(`(function() {
|
|
1240
|
-
var hosts = document.querySelectorAll("[data-composition-id]");
|
|
1241
|
-
var timelines = window.__timelines || {};
|
|
1242
|
-
var m = [];
|
|
1243
|
-
for (var i = 0; i < hosts.length; i++) {
|
|
1244
|
-
if (hosts[i].hasAttribute("data-no-timeline")) continue;
|
|
1245
|
-
var id = hosts[i].getAttribute("data-composition-id");
|
|
1246
|
-
if (id && !timelines[id]) m.push(id);
|
|
1247
|
-
}
|
|
1248
|
-
return m;
|
|
1249
|
-
})()`);
|
|
1250
|
-
// This block exists to BUILD A WARNING, so it must never be the thing that
|
|
1251
|
-
// throws. `page.evaluate` is loosely typed, and a caller that stubs it (or a
|
|
1252
|
-
// runtime that returns nothing here) would turn a blind `as string[]` cast
|
|
1253
|
-
// into a TypeError on the diagnostic path. Normalise instead of asserting.
|
|
1254
|
-
const pendingIds = Array.isArray(evaluated) ? evaluated.map((id) => String(id)) : [];
|
|
1255
1267
|
onPending?.(pendingIds);
|
|
1256
1268
|
const missing = pendingIds.join(", ");
|
|
1257
1269
|
if (scriptFailureBail) {
|
|
@@ -1733,9 +1745,7 @@ export async function initializeSession(session) {
|
|
|
1733
1745
|
const pageReadyTimeout = session.config?.playerReadyTimeout ?? DEFAULT_CONFIG.playerReadyTimeout;
|
|
1734
1746
|
await pollHfReady(page, pageReadyTimeout);
|
|
1735
1747
|
logInitPhase("pollHfReady complete");
|
|
1736
|
-
|
|
1737
|
-
session.pendingTimelineIds = [...ids];
|
|
1738
|
-
}, () => session.vfxFailure !== undefined);
|
|
1748
|
+
await waitForSubCompositionTimelines(session, page, pageReadyTimeout);
|
|
1739
1749
|
logInitPhase(`pollSubCompositionTimelines complete (${session.subTimelineWaitOutcome})`);
|
|
1740
1750
|
assertVfxIntact(session);
|
|
1741
1751
|
recordSubTimelineWarning(session, pageReadyTimeout);
|
|
@@ -1865,9 +1875,7 @@ export async function initializeSession(session) {
|
|
|
1865
1875
|
const pageReadyTimeout = session.config?.playerReadyTimeout ?? DEFAULT_CONFIG.playerReadyTimeout;
|
|
1866
1876
|
await pollHfReady(page, pageReadyTimeout);
|
|
1867
1877
|
logInitPhase("pollHfReady complete");
|
|
1868
|
-
|
|
1869
|
-
session.pendingTimelineIds = [...ids];
|
|
1870
|
-
}, () => session.vfxFailure !== undefined);
|
|
1878
|
+
await waitForSubCompositionTimelines(session, page, pageReadyTimeout);
|
|
1871
1879
|
logInitPhase(`pollSubCompositionTimelines complete (${session.subTimelineWaitOutcome})`);
|
|
1872
1880
|
assertVfxIntact(session);
|
|
1873
1881
|
recordSubTimelineWarning(session, pageReadyTimeout);
|
|
@@ -2465,7 +2473,10 @@ export async function createStaticVerificationPage(session) {
|
|
|
2465
2473
|
});
|
|
2466
2474
|
await page.evaluate(`window.__hfFlushSync?.()`);
|
|
2467
2475
|
await pollHfReady(page, pageReadyTimeout);
|
|
2468
|
-
await pollSubCompositionTimelines(page, pageReadyTimeout
|
|
2476
|
+
await pollSubCompositionTimelines(page, pageReadyTimeout, {
|
|
2477
|
+
getScriptLoadFailures: () => session.scriptLoadFailures,
|
|
2478
|
+
knownUnregisteredIds: session.subTimelineWaitMemo.unregisteredIds,
|
|
2479
|
+
});
|
|
2469
2480
|
await applyVideoMetadataHints(page, session.options.videoMetadataHints);
|
|
2470
2481
|
const skipVideoIds = session.options.skipReadinessVideoIds ?? [];
|
|
2471
2482
|
await Promise.all([
|
|
@@ -3472,10 +3483,8 @@ export async function captureFramesBatchPipelined(session, frameIndices, times)
|
|
|
3472
3483
|
* `captureFrameCore`.
|
|
3473
3484
|
*/
|
|
3474
3485
|
export async function discardWarmupCapture(session, frameIndex = 0, time = 0, innerCapture = captureFrameCore) {
|
|
3475
|
-
//
|
|
3476
|
-
|
|
3477
|
-
// numbers — no nested state to deep-copy.
|
|
3478
|
-
const perfBefore = { ...session.capturePerf };
|
|
3486
|
+
// The discarded capture mutates frameMs, so preserve its samples by value.
|
|
3487
|
+
const perfBefore = { ...session.capturePerf, frameMs: [...session.capturePerf.frameMs] };
|
|
3479
3488
|
const hasDamageBefore = session.beginFrameHasDamageCount;
|
|
3480
3489
|
const noDamageBefore = session.beginFrameNoDamageCount;
|
|
3481
3490
|
const dedupCountBefore = session.staticDedupCount;
|