Cache the page-owned app response before starting the worker
This commit is contained in:
@@ -8,12 +8,12 @@ const tagHash = bytes => createHash('sha256').update(bytes).digest('hex').slice(
|
||||
export const injectBuildTag = (source, tag) => source.replace(
|
||||
/typeof __BUILD_TAG__ !== 'undefined' \? __BUILD_TAG__ : '[^']*'/, JSON.stringify(tag));
|
||||
|
||||
function embedAssets(source, files, groups, buildTag, hashing) {
|
||||
function embedAssets(source, files, groups, buildTag, hashing, assetSync) {
|
||||
const pageFiles = {};
|
||||
for (const group of Object.values(groups)) for (const path of group.files) {
|
||||
if (path !== '/index.html' && path !== '/sw.js') pageFiles[path] = { ...files[path], h: hashing ? files[path].h : buildTag };
|
||||
}
|
||||
const json = JSON.stringify({ buildTag, groups, files: pageFiles }).replace(/</g, '\\u003c');
|
||||
const json = JSON.stringify({ buildTag, groups, files: pageFiles, appCache: assetSync }).replace(/</g, '\\u003c');
|
||||
return source.replace(/(<script type="application\/json" id="ytp-assets">)[\s\S]*?(<\/script>)/, (_, open, close) => open + json + close);
|
||||
}
|
||||
|
||||
@@ -70,16 +70,17 @@ export function createAssetManifest(publicDir = './public', { hashing = true, bu
|
||||
}
|
||||
const files = {};
|
||||
for (const url of [...bytes.keys()].sort()) files[url] = { h: assetHash(bytes.get(url)), s: bytes.get(url).length, g: membership.get(url) || 'core' };
|
||||
const source = bytes.get('/index.html')?.toString() ?? null;
|
||||
const source = bytes.get('/index.html')?.toString()
|
||||
.replaceAll('__APP_SCRIPT_HASH__', createHash('sha256').update(bytes.get('/app.js') || '').digest('base64')) ?? null;
|
||||
const swSource = bytes.get('/sw.js')?.toString() ?? null;
|
||||
// Derived build metadata cannot be an input to its own hash. Canonicalize
|
||||
// the index meta and SW injected tag, then publish hashes of the final bytes.
|
||||
// All original bytes (including index/SW source) remain inputs via source hashes.
|
||||
let canonicalIndex = source === null ? null : stampIndex(embedAssets(source, files, groups, '__BUILD_TAG__', hashing), files, { hashing });
|
||||
let canonicalIndex = source === null ? null : stampIndex(embedAssets(source, files, groups, '__BUILD_TAG__', hashing, assetSync), files, { hashing });
|
||||
const canonical = { assetSync, files: { ...files }, groups, contracts };
|
||||
if (canonicalIndex !== null && hashing) canonical.files['/index.html'] = { ...files['/index.html'], h: assetHash(canonicalIndex), s: Buffer.byteLength(canonicalIndex), source: files['/index.html'].h };
|
||||
const buildTag = hashing ? tagHash(JSON.stringify(canonical)) : (override || legacy.digest('hex').slice(0, 12));
|
||||
const index = source === null ? null : embedAssets(stampIndex(source, files, { hashing, buildTag }), files, groups, buildTag, hashing);
|
||||
const index = source === null ? null : embedAssets(stampIndex(source, files, { hashing, buildTag }), files, groups, buildTag, hashing, assetSync);
|
||||
const sw = swSource === null ? null : injectBuildTag(swSource, buildTag)
|
||||
.replace("typeof __ASSET_SYNC__ !== 'undefined' ? __ASSET_SYNC__ : true", JSON.stringify(assetSync))
|
||||
.replace("importScripts('/asset-sync-core.js')", "importScripts('/asset-sync-core.js?v=" + (files['/asset-sync-core.js']?.h || '') + "')");
|
||||
|
||||
@@ -88,3 +88,15 @@ test('index embeds build-local group hashes without recursively embedding index/
|
||||
const legacy=JSON.parse(off.index.match(/id="ytp-assets">([^<]+)<\/script>/)[1]);
|
||||
expect(legacy.files['/app.js'].h).toBe(off.manifest.buildTag);
|
||||
}));
|
||||
|
||||
test('app bootstrap CSP hash follows exact app bytes and rollback keeps external execution',()=>fixture(dir=>{
|
||||
writeFileSync(join(dir,'index.html'),'<meta http-equiv="Content-Security-Policy" content="script-src \'self\' \'sha256-__APP_SCRIPT_HASH__\'"><script type="application/json" id="ytp-assets">{}</script>');
|
||||
const result=createAssetManifest(dir),digest=createHash('sha256').update('window.app=1').digest('base64');
|
||||
expect(result.index).toContain("'sha256-"+digest+"'");
|
||||
|
||||
expect(JSON.parse(result.index.match(/id="ytp-assets">([^<]+)</)[1]).appCache).toBe(true);
|
||||
expect(JSON.parse(createAssetManifest(dir,{assetSync:false}).index.match(/id="ytp-assets">([^<]+)</)[1]).appCache).toBe(false);
|
||||
expect(JSON.parse(createAssetManifest(dir,{hashing:false}).index.match(/id="ytp-assets">([^<]+)</)[1]).appCache).toBe(false);
|
||||
writeFileSync(join(dir,'app.js'),'window.app=2');
|
||||
expect(createAssetManifest(dir).index).not.toContain(digest);
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user