mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
fix(core,producer): harden render reapply wiring
This commit is contained in:
@@ -399,13 +399,14 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build": "bun run build:hyperframes-runtime && bun run build:position-edits-render && tsc && tsx scripts/rewrite-esm-extensions.ts",
|
||||
"test": "vitest run",
|
||||
"test": "bun run check:position-edits-render && vitest run",
|
||||
"test:watch": "vitest",
|
||||
"test:coverage": "vitest run --coverage",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"lint:runtime-preview-guards": "tsx scripts/lint-runtime-preview-guards.ts",
|
||||
"build:hyperframes-runtime": "tsx scripts/build-hyperframes-runtime-artifact.ts",
|
||||
"build:position-edits-render": "tsx scripts/build-position-edits-render.ts",
|
||||
"check:position-edits-render": "bun run build:position-edits-render && git diff --exit-code -- src/generated/position-edits-render-inline.ts",
|
||||
"build:hyperframes-runtime:modular": "SANDBOX_RUNTIME_VARIANT=modular tsx scripts/build-hyperframes-runtime-artifact.ts",
|
||||
"build:hyperframe-runtime": "tsx scripts/build-hyperframes-runtime-artifact.ts",
|
||||
"test:hyperframe-runtime-contract": "tsx scripts/test-hyperframe-runtime-contract.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// AUTO-GENERATED by scripts/build-position-edits-render.ts - do not edit
|
||||
const POSITION_EDITS_RENDER_IIFE: string =
|
||||
'"use strict";(()=>{function g(){return globalThis}function w(t,e){if(typeof window>"u")return;let n=g(),o=n.__hf?.onSwallowed;if(o)try{o({label:t,error:e})}catch(r){}(n.__hfDebug||n.__HYPERFRAMES_DEBUG)&&console.debug(`[hyperframes] ${t} swallowed:`,e)}var E=null;function h(t,e){if(E)try{E({source:"hf-preview",type:"analytics",event:t,properties:e??{}})}catch(n){w("runtime.analytics.site1",n)}}var l="data-hf-edit-base-x",p="data-hf-edit-base-y",d="data-hf-edit-original-translate",u=t=>{let e=parseFloat(t??"");return Number.isFinite(e)?e:0},$=t=>{let e=[],n=0,o="";for(let r of t.trim())r==="("&&(n+=1),r===")"&&(n=Math.max(0,n-1)),/\\s/.test(r)&&n===0?(o&&e.push(o),o=""):o+=r;return o&&e.push(o),e},k=/^-?(?:\\d+(?:\\.\\d+)?|\\.\\d+)px$/,f=(t,e)=>k.test(t)&&k.test(e)?`${parseFloat(t)+parseFloat(e)}px`:`calc(${t} + ${e})`,D=(t,e,n)=>{if(!t||t==="none")return`${e} ${n}`;let[o,r,i]=$(t);if(o===void 0)return`${e} ${n}`;if(r===void 0)return`${f(o,e)} ${n}`;let a=i===void 0?"":` ${i}`;return`${f(o,e)} ${f(r,n)}${a}`},L=t=>{try{t.ownerDocument.defaultView?.gsap?.getProperty?.(t,"x")}catch{}},M=t=>{let e=t.style.getPropertyValue("translate").trim();if(e)return e==="none"?"":e;try{let n=t.ownerDocument.defaultView,o=n?n.getComputedStyle(t).getPropertyValue("translate").trim():"";return o==="none"?"":o}catch{return""}},A=new WeakMap;function H(t,e){let n=A.get(t);if(!e?.force&&n!==void 0&&t.style.getPropertyValue("translate")!==n){h("position_edit_fold_skipped",{hfId:t.getAttribute("data-hf-id")});return}let o=u(t.getAttribute("data-x"))-u(t.getAttribute(l)),r=u(t.getAttribute("data-y"))-u(t.getAttribute(p));t.hasAttribute(d)||t.setAttribute(d,M(t)),n===void 0&&L(t);let i=t.getAttribute(d)??"",a=D(i,`${o}px`,`${r}px`);t.style.setProperty("translate",a),A.set(t,t.style.getPropertyValue("translate"))}function _(t){let e=t.querySelectorAll(`[${l}], [${p}]`),n=t.defaultView?.HTMLElement,o=0;for(let r=0;r<e.length;r++){let i=e[r];(n?i instanceof n:typeof i.style?.setProperty=="function")&&(H(i),o+=1)}return o}var T="__hfPositionEditsSeekReapplyWrapped";function v(t){let e=t,n=()=>{try{_(e.document)}catch{}},o=s=>typeof s=="function"&&!!s[T],r=s=>{try{Object.defineProperty(s,T,{value:!0})}catch{}},i=(s,P)=>{let c=s();if(typeof c!="function"||o(c))return;let m=function(...R){let x=c.apply(this,R);return n(),x};r(m),P(m),n()},a=()=>{i(()=>e.__hf?.seek,s=>{e.__hf&&(e.__hf.seek=s)}),i(()=>e.__player?.renderSeek,s=>{e.__player&&(e.__player.renderSeek=s)})};a();let y=120,S=e.setInterval(()=>{a(),y-=1,y<=0&&e.clearInterval(S)},50)}function b(){document.querySelector(`[${l}], [${p}]`)&&(_(document),v(window))}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",b,{once:!0}):b();})();\n';
|
||||
'"use strict";(()=>{function v(){return globalThis}function A(e,t){if(typeof window>"u")return;let o=v(),r=o.__hf?.onSwallowed;if(r)try{r({label:e,error:t})}catch(i){}(o.__hfDebug||o.__HYPERFRAMES_DEBUG)&&console.debug(`[hyperframes] ${e} swallowed:`,t)}var P=null;function T(e,t){if(P)try{P({source:"hf-preview",type:"analytics",event:e,properties:t??{}})}catch(o){A("runtime.analytics.site1",o)}}var w="data-hf-edit-base-x",y="data-hf-edit-base-y",_="data-hf-edit-original-translate",g=e=>{let t=parseFloat(e??"");return Number.isFinite(t)?t:0},H=e=>{let t=[],o=0,r="";for(let i of e.trim())i==="("&&(o+=1),i===")"&&(o=Math.max(0,o-1)),/\\s/.test(i)&&o===0?(r&&t.push(r),r=""):r+=i;return r&&t.push(r),t},R=/^-?(?:\\d+(?:\\.\\d+)?|\\.\\d+)px$/,b=(e,t)=>R.test(e)&&R.test(t)?`${parseFloat(e)+parseFloat(t)}px`:`calc(${e} + ${t})`,I=(e,t,o)=>{if(!e||e==="none")return`${t} ${o}`;let[r,i,a]=H(e);if(r===void 0)return`${t} ${o}`;if(i===void 0)return`${b(r,t)} ${o}`;let c=a===void 0?"":` ${a}`;return`${b(r,t)} ${b(i,o)}${c}`},O=e=>{try{e.ownerDocument.defaultView?.gsap?.getProperty?.(e,"x")}catch{}},j=e=>{let t=e.style.getPropertyValue("translate").trim();if(t)return t==="none"?"":t;try{let o=e.ownerDocument.defaultView,r=o?o.getComputedStyle(e).getPropertyValue("translate").trim():"";return r==="none"?"":r}catch{return""}},x=new WeakMap;function V(e,t){let o=x.get(e);if(!t?.force&&o!==void 0&&e.style.getPropertyValue("translate")!==o){T("position_edit_fold_skipped",{hfId:e.getAttribute("data-hf-id")});return}let r=g(e.getAttribute("data-x"))-g(e.getAttribute(w)),i=g(e.getAttribute("data-y"))-g(e.getAttribute(y));e.hasAttribute(_)||e.setAttribute(_,j(e)),o===void 0&&O(e);let a=e.getAttribute(_)??"",c=I(a,`${r}px`,`${i}px`);e.style.setProperty("translate",c),x.set(e,e.style.getPropertyValue("translate"))}function m(e){let t=e.querySelectorAll(`[${w}], [${y}]`),o=e.defaultView?.HTMLElement,r=0;for(let i=0;i<t.length;i++){let a=t[i];(o?a instanceof o:typeof a.style?.setProperty=="function")&&(V(a),r+=1)}return r}var D="__hfPositionEditsSeekReapplyWrapped",$=new WeakSet,L=new WeakMap,F=new WeakMap;function M(e){let t=e,o=()=>{try{m(t.document)}catch{}},r=n=>typeof n=="function"&&($.has(n)||!!n[D]),i=n=>{$.add(n);try{Object.defineProperty(n,D,{value:!0})}catch{}},a=n=>{if(typeof n!="function"||r(n))return n;let s=function(...u){let d=n.apply(this,u);return o(),d};return i(s),s},c=(n,s)=>{let u=L.get(n);if(u?.has(s))return!0;let d=Object.getOwnPropertyDescriptor(n,s);if(d?.configurable===!1){let l=n[s];return typeof l=="function"&&(n[s]=a(l),o()),!1}let p=n[s],f=d?.set;return Object.defineProperty(n,s,{configurable:!0,enumerable:d?.enumerable??!0,get:()=>p,set:l=>{p=a(l),f?.call(n,l)}}),p=a(p),u??(u=new Set),u.add(s),L.set(n,u),o(),!0},k=(n,s)=>{let u=F.get(t),d=Object.getOwnPropertyDescriptor(t,n);if(!u?.has(n)){if(d?.configurable===!1){let l=t[n];return l?c(l,s):!1}let f=t[n];Object.defineProperty(t,n,{configurable:!0,enumerable:d?.enumerable??!0,get:()=>f,set:l=>{f=l,f&&c(f,s)}}),u??(u=new Set),u.add(n),F.set(t,u)}let p=t[n];return p?c(p,s):!1},E=()=>{let n=k("__hf","seek"),s=k("__player","renderSeek");return n&&s};if(E())return;let S=120,h=t.setInterval(()=>{if(E()){t.clearInterval(h);return}S-=1,S<=0&&t.clearInterval(h)},50)}function W(){document.querySelector(`[${w}], [${y}]`)&&(m(document),M(window))}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",W,{once:!0}):W();})();\n';
|
||||
|
||||
/** Returns the pre-built position-edits render IIFE as a string constant. */
|
||||
export function getPositionEditsRenderScript(): string {
|
||||
|
||||
@@ -177,6 +177,10 @@ export function applyPositionEdits(doc: Document): number {
|
||||
}
|
||||
|
||||
const SEEK_REAPPLY_WRAPPED = "__hfPositionEditsSeekReapplyWrapped";
|
||||
type SeekFunction = (...args: unknown[]) => unknown;
|
||||
const wrappedSeekFunctions = new WeakSet<SeekFunction>();
|
||||
const observedSeekProperties = new WeakMap<object, Set<string>>();
|
||||
const observedGlobalProperties = new WeakMap<object, Set<string>>();
|
||||
|
||||
type SeekWindow = Window &
|
||||
typeof globalThis & {
|
||||
@@ -195,54 +199,105 @@ export function installPositionEditsSeekReapply(win: Window & typeof globalThis)
|
||||
}
|
||||
};
|
||||
|
||||
const isWrapped = (fn: unknown): fn is (...args: unknown[]) => unknown =>
|
||||
const isWrapped = (fn: unknown): fn is SeekFunction =>
|
||||
typeof fn === "function" &&
|
||||
Boolean((fn as { [SEEK_REAPPLY_WRAPPED]?: boolean })[SEEK_REAPPLY_WRAPPED]);
|
||||
(wrappedSeekFunctions.has(fn) ||
|
||||
Boolean((fn as { [SEEK_REAPPLY_WRAPPED]?: boolean })[SEEK_REAPPLY_WRAPPED]));
|
||||
|
||||
const markWrapped = (fn: (...args: unknown[]) => unknown): void => {
|
||||
const markWrapped = (fn: SeekFunction): void => {
|
||||
wrappedSeekFunctions.add(fn);
|
||||
try {
|
||||
Object.defineProperty(fn, SEEK_REAPPLY_WRAPPED, { value: true });
|
||||
} catch {
|
||||
// Frozen functions cannot be marked; the wrapper itself is still valid.
|
||||
// The WeakSet keeps frozen functions from being wrapped repeatedly.
|
||||
}
|
||||
};
|
||||
|
||||
const wrapOne = (
|
||||
get: () => unknown,
|
||||
set: (fn: (...args: unknown[]) => unknown) => void,
|
||||
): void => {
|
||||
const fn = get();
|
||||
if (typeof fn !== "function") return;
|
||||
if (isWrapped(fn)) return;
|
||||
const wrapped = function (this: unknown, ...args: unknown[]): unknown {
|
||||
const wrapFunction = (fn: unknown): unknown => {
|
||||
if (typeof fn !== "function" || isWrapped(fn)) return fn;
|
||||
const wrapped: SeekFunction = function (this: unknown, ...args: unknown[]): unknown {
|
||||
const result = fn.apply(this, args);
|
||||
reapply();
|
||||
return result;
|
||||
};
|
||||
markWrapped(wrapped);
|
||||
set(wrapped);
|
||||
return wrapped;
|
||||
};
|
||||
|
||||
const observeSeekProperty = (container: object, property: string): boolean => {
|
||||
let observed = observedSeekProperties.get(container);
|
||||
if (observed?.has(property)) return true;
|
||||
const descriptor = Object.getOwnPropertyDescriptor(container, property);
|
||||
if (descriptor?.configurable === false) {
|
||||
const current = (container as Record<string, unknown>)[property];
|
||||
if (typeof current === "function") {
|
||||
(container as Record<string, unknown>)[property] = wrapFunction(current);
|
||||
reapply();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
let current = (container as Record<string, unknown>)[property];
|
||||
const originalSetter = descriptor?.set;
|
||||
Object.defineProperty(container, property, {
|
||||
configurable: true,
|
||||
enumerable: descriptor?.enumerable ?? true,
|
||||
get: () => current,
|
||||
set: (value: unknown) => {
|
||||
current = wrapFunction(value);
|
||||
originalSetter?.call(container, value);
|
||||
},
|
||||
});
|
||||
current = wrapFunction(current);
|
||||
observed ??= new Set<string>();
|
||||
observed.add(property);
|
||||
observedSeekProperties.set(container, observed);
|
||||
reapply();
|
||||
return true;
|
||||
};
|
||||
|
||||
const wrapAll = (): void => {
|
||||
wrapOne(
|
||||
() => target.__hf?.seek,
|
||||
(fn) => {
|
||||
if (target.__hf) target.__hf.seek = fn;
|
||||
},
|
||||
);
|
||||
wrapOne(
|
||||
() => target.__player?.renderSeek,
|
||||
(fn) => {
|
||||
if (target.__player) target.__player.renderSeek = fn;
|
||||
},
|
||||
);
|
||||
const observeGlobalContainer = (
|
||||
name: "__hf" | "__player",
|
||||
property: "seek" | "renderSeek",
|
||||
): boolean => {
|
||||
let globals = observedGlobalProperties.get(target);
|
||||
const descriptor = Object.getOwnPropertyDescriptor(target, name);
|
||||
if (!globals?.has(name)) {
|
||||
if (descriptor?.configurable === false) {
|
||||
const current = target[name];
|
||||
return current ? observeSeekProperty(current, property) : false;
|
||||
}
|
||||
let value = target[name];
|
||||
Object.defineProperty(target, name, {
|
||||
configurable: true,
|
||||
enumerable: descriptor?.enumerable ?? true,
|
||||
get: () => value,
|
||||
set: (next: unknown) => {
|
||||
value = next as typeof value;
|
||||
if (value) observeSeekProperty(value, property);
|
||||
},
|
||||
});
|
||||
globals ??= new Set<string>();
|
||||
globals.add(name);
|
||||
observedGlobalProperties.set(target, globals);
|
||||
}
|
||||
const current = target[name];
|
||||
return current ? observeSeekProperty(current, property) : false;
|
||||
};
|
||||
|
||||
wrapAll();
|
||||
const wrapAll = (): boolean => {
|
||||
const hfObserved = observeGlobalContainer("__hf", "seek");
|
||||
const playerObserved = observeGlobalContainer("__player", "renderSeek");
|
||||
return hfObserved && playerObserved;
|
||||
};
|
||||
|
||||
if (wrapAll()) return;
|
||||
let remaining = 120;
|
||||
const interval = target.setInterval(() => {
|
||||
wrapAll();
|
||||
if (wrapAll()) {
|
||||
target.clearInterval(interval);
|
||||
return;
|
||||
}
|
||||
remaining -= 1;
|
||||
if (remaining <= 0) target.clearInterval(interval);
|
||||
}, 50);
|
||||
|
||||
@@ -89,7 +89,8 @@ export function injectSdkPositionEditsRenderScript(html: string): string {
|
||||
if (!html.includes("data-hf-edit-base-x") && !html.includes("data-hf-edit-base-y")) {
|
||||
return html;
|
||||
}
|
||||
const script = `<script>${getPositionEditsRenderScript()}</script>`;
|
||||
const scriptBody = getPositionEditsRenderScript().replace(/<\/script/gi, "<\\/script");
|
||||
const script = `<script>${scriptBody}</script>`;
|
||||
const bodyClose = html.search(/<\/body\s*>/i);
|
||||
if (bodyClose < 0) return `${html}${script}`;
|
||||
return `${html.slice(0, bodyClose)}${script}${html.slice(bodyClose)}`;
|
||||
|
||||
Reference in New Issue
Block a user