fix(core,producer): harden render reapply wiring

This commit is contained in:
Vance Ingalls
2026-07-09 17:38:31 -07:00
parent 4e3d639e1a
commit 68ae4e5160
4 changed files with 88 additions and 31 deletions
+2 -1
View File
@@ -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 {
+83 -28
View File
@@ -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)}`;