v8Cache: initial add
This commit is contained in:
parent
02cce39e44
commit
e1bb814c88
52
gluon.d.ts
vendored
52
gluon.d.ts
vendored
@ -1,3 +1,55 @@
|
|||||||
|
type V8CacheBuildOptions = {
|
||||||
|
/**
|
||||||
|
* Path to save the V8 Cache to. Defaults to v8Cache.json in Gluon's browser data.
|
||||||
|
*/
|
||||||
|
path?: string,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use eager compilation.
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
eager?: Boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URLs of scripts to cache. Defaults to automatically detecting currently loaded scripts in the page.
|
||||||
|
*/
|
||||||
|
urls?: string[],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reload the page to force script compilation.
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
reload?: Boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Include preload scripts in automatic detection.
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
includePreload?: Boolean
|
||||||
|
};
|
||||||
|
|
||||||
|
type V8CacheApi = {
|
||||||
|
/** Build a V8 Cache. */
|
||||||
|
build(
|
||||||
|
/** Build options. */
|
||||||
|
options?: V8CacheBuildOptions
|
||||||
|
),
|
||||||
|
|
||||||
|
/** Load a V8 Cache. */
|
||||||
|
load(
|
||||||
|
/**
|
||||||
|
* Path to load. Defaults to v8Cache.json in Gluon's browser data.
|
||||||
|
*/
|
||||||
|
path?: string
|
||||||
|
): Promise<boolean>
|
||||||
|
|
||||||
|
/** Check if a V8 Cache exists with a given path. */
|
||||||
|
exists(
|
||||||
|
/** Path to check. */
|
||||||
|
path: string
|
||||||
|
): Promise<Boolean>
|
||||||
|
};
|
||||||
|
|
||||||
type PageApi = {
|
type PageApi = {
|
||||||
/**
|
/**
|
||||||
* Evaluate a string or function in the web context.
|
* Evaluate a string or function in the web context.
|
||||||
|
102
src/api/v8Cache.js
Normal file
102
src/api/v8Cache.js
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import { join, sep } from 'path';
|
||||||
|
import { access, writeFile, readFile } from 'fs/promises';
|
||||||
|
import { log } from '../lib/logger.js';
|
||||||
|
|
||||||
|
export default async (CDP, evaluate, { browserType, dataPath }) => {
|
||||||
|
if (browserType !== 'chromium') { // current implementation is for chromium-based only
|
||||||
|
const warning = () => log(`Warning: V8 Cache API is only for Chromium (running on ${browserType})`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
build: () => {},
|
||||||
|
load: () => false,
|
||||||
|
exists: () => false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await CDP.send('Page.enable');
|
||||||
|
|
||||||
|
const getDefaultPath = () => join(dataPath, 'v8Cache.json');
|
||||||
|
const getScriptUrls = async (includePreload = true) => (await evaluate(`[...document.querySelectorAll('script[src]${includePreload ? ', link[as=script]' : ''}')].map(x => x.src ?? x.href).join(';')`)).split(';');
|
||||||
|
|
||||||
|
const build = async ({ path = getDefaultPath(), eager = true, urls = [], reload = true, includePreload = true, finishOnLoad = true } = {}) => {
|
||||||
|
const startTime = performance.now();
|
||||||
|
|
||||||
|
log('v8Cache: beginning cache build...');
|
||||||
|
urls ??= await getScriptUrls(includePreload);
|
||||||
|
|
||||||
|
log(`v8Cache: found ${urls.length} scripts`);
|
||||||
|
|
||||||
|
const cache = await new Promise(async resolve => {
|
||||||
|
let produced = [], done = false;
|
||||||
|
|
||||||
|
const unhook = CDP.on('Page.compilationCacheProduced', ({ params: { url, data }}) => {
|
||||||
|
// console.log('produced', url);
|
||||||
|
produced.push({ url, data });
|
||||||
|
|
||||||
|
process.stdout.write(`v8Cache: caching... (${produced.length}/${urls.length})\r`);
|
||||||
|
|
||||||
|
if (produced.length >= urls.length) {
|
||||||
|
done = true;
|
||||||
|
unhook();
|
||||||
|
resolve(produced);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await CDP.send('Page.produceCompilationCache', {
|
||||||
|
scripts: urls.map(url => ({ url, eager }))
|
||||||
|
});
|
||||||
|
|
||||||
|
if (reload) {
|
||||||
|
if (finishOnLoad) CDP.on('Page.frameStoppedLoading', async () => {
|
||||||
|
// console.log('loaded');
|
||||||
|
await new Promise(res => setTimeout(res, 2000));
|
||||||
|
if (done) return;
|
||||||
|
|
||||||
|
log('v8Cache: forcing caching to end early as loading is done');
|
||||||
|
|
||||||
|
unhook();
|
||||||
|
resolve(produced);
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
log('v8Cache: reloading to force script loads...');
|
||||||
|
await CDP.send('Page.reload');
|
||||||
|
}
|
||||||
|
|
||||||
|
process.stdout.write(`v8Cache: starting cache...\r`);
|
||||||
|
});
|
||||||
|
|
||||||
|
log(`v8Cache: cached ${cache.length}/${urls.length} scripts in ${(performance.now() - startTime).toFixed(2)}ms`);
|
||||||
|
|
||||||
|
const raw = JSON.stringify(cache, null, 2);
|
||||||
|
await writeFile(path, raw);
|
||||||
|
|
||||||
|
log(`v8Cache: saved to .../${path.split(sep).slice(-3).join('/')} (${(Buffer.byteLength(raw, 'utf8') / 1024 / 1024).toFixed(2)}MB)`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const exists = (path = getDefaultPath()) => access(path).then(() => true).catch(() => false);
|
||||||
|
|
||||||
|
const load = async (path = getDefaultPath()) => {
|
||||||
|
if (!await exists(path)) return false;
|
||||||
|
const startTime = performance.now();
|
||||||
|
|
||||||
|
const cache = JSON.parse(await readFile(path, 'utf8'));
|
||||||
|
|
||||||
|
for (const entry of cache) {
|
||||||
|
// console.log('loaded', entry);
|
||||||
|
CDP.send('Page.addCompilationCache', entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
log(`v8Cache: loaded ${cache.length} scripts in ${(performance.now() - startTime).toFixed(2)}ms`);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// try to load default ASAP
|
||||||
|
load();
|
||||||
|
|
||||||
|
return {
|
||||||
|
build,
|
||||||
|
load,
|
||||||
|
exists
|
||||||
|
};
|
||||||
|
};
|
@ -206,7 +206,8 @@ const startBrowser = async (url, { windowSize, forceBrowser, forceEngine }) => {
|
|||||||
localUrl,
|
localUrl,
|
||||||
openingLocal,
|
openingLocal,
|
||||||
closeHandlers,
|
closeHandlers,
|
||||||
browserType
|
browserType,
|
||||||
|
dataPath
|
||||||
});
|
});
|
||||||
|
|
||||||
return Window;
|
return Window;
|
||||||
|
@ -5,6 +5,7 @@ import LocalCDP from '../lib/local/cdp.js';
|
|||||||
|
|
||||||
import IdleApi from '../api/idle.js';
|
import IdleApi from '../api/idle.js';
|
||||||
import ControlsApi from '../api/controls.js';
|
import ControlsApi from '../api/controls.js';
|
||||||
|
import V8CacheApi from '../api/v8Cache.js';
|
||||||
|
|
||||||
const acquireTarget = async (CDP, filter = () => true) => {
|
const acquireTarget = async (CDP, filter = () => true) => {
|
||||||
let target;
|
let target;
|
||||||
@ -25,7 +26,7 @@ const acquireTarget = async (CDP, filter = () => true) => {
|
|||||||
})).sessionId;
|
})).sessionId;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async (CDP, proc, injectionType = 'browser', { browserName, browserType, openingLocal, localUrl, url, closeHandlers }) => {
|
export default async (CDP, proc, injectionType = 'browser', { dataPath, browserName, browserType, openingLocal, localUrl, url, closeHandlers }) => {
|
||||||
let pageLoadCallback, pageLoadPromise = new Promise(res => pageLoadCallback = res);
|
let pageLoadCallback, pageLoadPromise = new Promise(res => pageLoadCallback = res);
|
||||||
let frameLoadCallback = () => {}, onWindowMessage = () => {};
|
let frameLoadCallback = () => {}, onWindowMessage = () => {};
|
||||||
CDP.onMessage(msg => {
|
CDP.onMessage(msg => {
|
||||||
@ -125,6 +126,7 @@ export default async (CDP, proc, injectionType = 'browser', { browserName, brows
|
|||||||
|
|
||||||
Window.idle = await IdleApi(Window.cdp, { browserType, closeHandlers });
|
Window.idle = await IdleApi(Window.cdp, { browserType, closeHandlers });
|
||||||
Window.controls = await ControlsApi(Window.cdp);
|
Window.controls = await ControlsApi(Window.cdp);
|
||||||
|
Window.v8Cache = await V8CacheApi(Window.cdp, evalInWindow, { browserType, dataPath });
|
||||||
|
|
||||||
return Window;
|
return Window;
|
||||||
};
|
};
|
Loading…
x
Reference in New Issue
Block a user