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 = {
|
||||
/**
|
||||
* 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,
|
||||
openingLocal,
|
||||
closeHandlers,
|
||||
browserType
|
||||
browserType,
|
||||
dataPath
|
||||
});
|
||||
|
||||
return Window;
|
||||
|
@ -5,6 +5,7 @@ import LocalCDP from '../lib/local/cdp.js';
|
||||
|
||||
import IdleApi from '../api/idle.js';
|
||||
import ControlsApi from '../api/controls.js';
|
||||
import V8CacheApi from '../api/v8Cache.js';
|
||||
|
||||
const acquireTarget = async (CDP, filter = () => true) => {
|
||||
let target;
|
||||
@ -25,7 +26,7 @@ const acquireTarget = async (CDP, filter = () => true) => {
|
||||
})).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 frameLoadCallback = () => {}, onWindowMessage = () => {};
|
||||
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.controls = await ControlsApi(Window.cdp);
|
||||
Window.v8Cache = await V8CacheApi(Window.cdp, evalInWindow, { browserType, dataPath });
|
||||
|
||||
return Window;
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user