Pin executing groups and guard lazy loads by core contracts
This commit is contained in:
@@ -7,7 +7,30 @@
|
||||
catch { return; } // Native/static shells retain their eager script path.
|
||||
const assets = new Map(), groups = new Map(), hooks = new Map();
|
||||
const css = new Set([...doc.querySelectorAll('link[data-lazy-css]')].map(link => link.dataset.lazyCss));
|
||||
const url = path => manifest.files[path]?.h ? path + '?v=' + manifest.files[path].h : path;
|
||||
const selected = new Map();
|
||||
const url = path => selected.get(path) || (manifest.files[path]?.h ? path + '?v=' + manifest.files[path].h : path);
|
||||
function reloadRequired(name) {
|
||||
api.reloadRequired = true;
|
||||
if (root.CustomEvent) root.dispatchEvent?.(new root.CustomEvent('ytp-reload-required', { detail: { group: name } }));
|
||||
}
|
||||
async function select(name) {
|
||||
if (!root.caches) return;
|
||||
try {
|
||||
const cache = await root.caches.open('ytplayer-assets');
|
||||
const response = await cache.match('/__ytp_asset_state');
|
||||
const current = response && (await response.json()).current;
|
||||
const built = manifest.groups[name], newer = current?.groups[name];
|
||||
if (!newer || current.buildTag === manifest.buildTag) return;
|
||||
if (newer.contract !== built.contract) { reloadRequired(name); return; }
|
||||
for (const path of built.files) {
|
||||
// Membership and contract must both match the running core's expectation.
|
||||
if (!newer.files.includes(path) || !current.files[path]) continue;
|
||||
const key = path + '?v=' + current.files[path].h;
|
||||
const held = await cache.match(key);
|
||||
if (held?.headers.get('X-Asset-Hash') === current.files[path].h) selected.set(path, key);
|
||||
}
|
||||
} catch { /* Private browsing/cache eviction: keep the embedded build-local map. */ }
|
||||
}
|
||||
function inject(path) {
|
||||
if (assets.has(path)) return assets.get(path);
|
||||
const style = /\.css$/.test(path);
|
||||
@@ -31,6 +54,7 @@
|
||||
if (!group) return Promise.reject(new Error('Unknown asset group: ' + name));
|
||||
const task = (async () => {
|
||||
for (const dependency of group.dependencies || []) await load(dependency);
|
||||
await select(name);
|
||||
for (const path of group.files) if (!/-worker\.js$/.test(path)) await inject(path);
|
||||
for (const fn of hooks.get(name) || []) await fn();
|
||||
group.executed = true;
|
||||
@@ -38,13 +62,13 @@
|
||||
groups.set(name, task);
|
||||
task.catch(error => {
|
||||
groups.delete(name);
|
||||
root.dispatchEvent?.(new root.CustomEvent('ytp-lazy-error', { detail: { group: name, error } }));
|
||||
if (root.CustomEvent) root.dispatchEvent?.(new root.CustomEvent('ytp-lazy-error', { detail: { group: name, error } }));
|
||||
});
|
||||
return task;
|
||||
}
|
||||
const api = {
|
||||
load, prefetch: load, loaded: name => !!manifest.groups[name]?.executed, url,
|
||||
manifest, layout: 'classic',
|
||||
manifest, layout: 'classic', reloadRequired: false,
|
||||
onLoad(name, fn) { const list = hooks.get(name) || []; list.push(fn); hooks.set(name, list); },
|
||||
proxy(name, methods, globalName) {
|
||||
return Object.fromEntries(methods.map(method => [method, async (...args) => {
|
||||
|
||||
Reference in New Issue
Block a user