diff --git a/_/amazon/handlers/index.js b/_/amazon/handlers/index.js index 3795b30..e1cddf6 100644 --- a/_/amazon/handlers/index.js +++ b/_/amazon/handlers/index.js @@ -36,7 +36,7 @@ exports.handler = async (event, context) => { } if (job.expected.hasOwnProperty('screenshot') === true) { - if (job.expected.hasOwnProperty('remove') === true ) { + if (job.expected.hasOwnProperty('remove') === true) { await page.evaluate((selector) => { document.getElementById(selector).remove(); }, job.expected.remove); diff --git a/source/helper.ts b/source/helper.ts index 2b1d76a..d6c87c1 100644 --- a/source/helper.ts +++ b/source/helper.ts @@ -10,27 +10,29 @@ interface FollowRedirOptions extends UrlWithStringQuery { } export const isValidUrl = (input: string) => { - try { - return !!new URL(input); - } catch (err) { - return false; - } -} + try { + return !!new URL(input); + } catch (err) { + return false; + } +}; export const downloadAndExtract = async (url: string) => - new Promise((resolve, reject) => { - const getOptions = parse(url) as FollowRedirOptions; - getOptions.maxBodyLength = 60 * 1024 * 1024; // 60mb - const destDir = `${tmpdir()}/chromium-pack` - const extractObj = extract(destDir) - https.get(url, (response) => { - response.pipe(extractObj); - extractObj.on('finish', () => { - resolve(destDir); - }); - }).on('error', (err) => { - unlink(destDir, (_) => { - reject(err) - }); + new Promise((resolve, reject) => { + const getOptions = parse(url) as FollowRedirOptions; + getOptions.maxBodyLength = 60 * 1024 * 1024; // 60mb + const destDir = `${tmpdir()}/chromium-pack`; + const extractObj = extract(destDir); + https + .get(url, (response) => { + response.pipe(extractObj); + extractObj.on("finish", () => { + resolve(destDir); }); - }) + }) + .on("error", (err) => { + unlink(destDir, (_) => { + reject(err); + }); + }); + }); diff --git a/source/index.ts b/source/index.ts index 3ad8b87..a1cc8c4 100644 --- a/source/index.ts +++ b/source/index.ts @@ -1,9 +1,15 @@ -import { access, createWriteStream, existsSync, mkdirSync, symlink } from 'node:fs'; -import { IncomingMessage } from 'node:http'; -import LambdaFS from './lambdafs'; -import { join } from 'node:path'; -import { URL } from 'node:url'; -import { downloadAndExtract, isValidUrl } from './helper'; +import { + access, + createWriteStream, + existsSync, + mkdirSync, + symlink, +} from "node:fs"; +import { IncomingMessage } from "node:http"; +import LambdaFS from "./lambdafs"; +import { join } from "node:path"; +import { URL } from "node:url"; +import { downloadAndExtract, isValidUrl } from "./helper"; /** Viewport taken from https://github.com/puppeteer/puppeteer/blob/main/docs/api/puppeteer.viewport.md */ interface Viewport { @@ -38,15 +44,21 @@ interface Viewport { hasTouch?: boolean; } -if ( process.env.AWS_EXECUTION_ENV !== undefined && /^AWS_Lambda_nodejs(?:14|16|18)[.]x$/.test(process.env.AWS_EXECUTION_ENV) === true) { +if ( + process.env.AWS_EXECUTION_ENV !== undefined && + /^AWS_Lambda_nodejs(?:14|16|18)[.]x$/.test(process.env.AWS_EXECUTION_ENV) === + true +) { if (process.env.FONTCONFIG_PATH === undefined) { - process.env.FONTCONFIG_PATH = '/tmp/aws'; + process.env.FONTCONFIG_PATH = "/tmp/aws"; } if (process.env.LD_LIBRARY_PATH === undefined) { - process.env.LD_LIBRARY_PATH = '/tmp/aws/lib'; - } else if (process.env.LD_LIBRARY_PATH.startsWith('/tmp/aws/lib') !== true) { - process.env.LD_LIBRARY_PATH = [...new Set(['/tmp/aws/lib', ...process.env.LD_LIBRARY_PATH.split(':')])].join(':'); + process.env.LD_LIBRARY_PATH = "/tmp/aws/lib"; + } else if (process.env.LD_LIBRARY_PATH.startsWith("/tmp/aws/lib") !== true) { + process.env.LD_LIBRARY_PATH = [ + ...new Set(["/tmp/aws/lib", ...process.env.LD_LIBRARY_PATH.split(":")]), + ].join(":"); } } @@ -63,7 +75,7 @@ class Chromium { } if (process.env.HOME === undefined) { - process.env.HOME = '/tmp'; + process.env.HOME = "/tmp"; } if (existsSync(`${process.env.HOME}/.fonts`) !== true) { @@ -76,24 +88,29 @@ class Chromium { } const url = new URL(input); - const output = `${process.env.HOME}/.fonts/${url.pathname.split('/').pop()}`; + const output = `${process.env.HOME}/.fonts/${url.pathname + .split("/") + .pop()}`; if (existsSync(output) === true) { - return resolve(output.split('/').pop() as string); + return resolve(output.split("/").pop() as string); } - if (url.protocol === 'file:') { + if (url.protocol === "file:") { access(url.pathname, (error) => { if (error != null) { return reject(error); } symlink(url.pathname, output, (error) => { - return error != null ? reject(error) : resolve(url.pathname.split('/').pop() as string); + return error != null + ? reject(error) + : resolve(url.pathname.split("/").pop() as string); }); }); } else { - let handler = url.protocol === 'http:' ? require('http').get : require('https').get; + let handler = + url.protocol === "http:" ? require("http").get : require("https").get; handler(input, (response: IncomingMessage) => { if (response.statusCode !== 200) { @@ -102,17 +119,17 @@ class Chromium { const stream = createWriteStream(output); - stream.once('error', (error) => { + stream.once("error", (error) => { return reject(error); }); - response.on('data', (chunk) => { + response.on("data", (chunk) => { stream.write(chunk); }); - response.once('end', () => { + response.once("end", () => { stream.end(() => { - return resolve(url.pathname.split('/').pop() as string); + return resolve(url.pathname.split("/").pop() as string); }); }); }); @@ -126,39 +143,39 @@ class Chromium { */ static get args(): string[] { const result = [ - '--allow-running-insecure-content', // https://source.chromium.org/search?q=lang:cpp+symbol:kAllowRunningInsecureContent&ss=chromium - '--autoplay-policy=user-gesture-required', // https://source.chromium.org/search?q=lang:cpp+symbol:kAutoplayPolicy&ss=chromium - '--disable-background-timer-throttling', - '--disable-component-update', // https://source.chromium.org/search?q=lang:cpp+symbol:kDisableComponentUpdate&ss=chromium - '--disable-domain-reliability', // https://source.chromium.org/search?q=lang:cpp+symbol:kDisableDomainReliability&ss=chromium - '--disable-features=AudioServiceOutOfProcess,IsolateOrigins,site-per-process', // https://source.chromium.org/search?q=file:content_features.cc&ss=chromium - '--disable-ipc-flooding-protection', - '--disable-print-preview', // https://source.chromium.org/search?q=lang:cpp+symbol:kDisablePrintPreview&ss=chromium - '--disable-dev-shm-usage', - '--disable-setuid-sandbox', // https://source.chromium.org/search?q=lang:cpp+symbol:kDisableSetuidSandbox&ss=chromium - '--disable-site-isolation-trials', // https://source.chromium.org/search?q=lang:cpp+symbol:kDisableSiteIsolation&ss=chromium - '--disable-speech-api', // https://source.chromium.org/search?q=lang:cpp+symbol:kDisableSpeechAPI&ss=chromium - '--disable-web-security', // https://source.chromium.org/search?q=lang:cpp+symbol:kDisableWebSecurity&ss=chromium - '--disk-cache-size=33554432', // https://source.chromium.org/search?q=lang:cpp+symbol:kDiskCacheSize&ss=chromium - '--enable-features=SharedArrayBuffer', // https://source.chromium.org/search?q=file:content_features.cc&ss=chromium - '--hide-scrollbars', // https://source.chromium.org/search?q=lang:cpp+symbol:kHideScrollbars&ss=chromium - '--ignore-gpu-blocklist', // https://source.chromium.org/search?q=lang:cpp+symbol:kIgnoreGpuBlocklist&ss=chromium - '--in-process-gpu', // https://source.chromium.org/search?q=lang:cpp+symbol:kInProcessGPU&ss=chromium - '--mute-audio', // https://source.chromium.org/search?q=lang:cpp+symbol:kMuteAudio&ss=chromium - '--no-default-browser-check', // https://source.chromium.org/search?q=lang:cpp+symbol:kNoDefaultBrowserCheck&ss=chromium - '--no-first-run', - '--no-pings', // https://source.chromium.org/search?q=lang:cpp+symbol:kNoPings&ss=chromium - '--no-sandbox', // https://source.chromium.org/search?q=lang:cpp+symbol:kNoSandbox&ss=chromium - '--no-zygote', // https://source.chromium.org/search?q=lang:cpp+symbol:kNoZygote&ss=chromium - '--use-gl=angle', // https://chromium.googlesource.com/chromium/src/+/main/docs/gpu/swiftshader.md - '--use-angle=swiftshader', // https://chromium.googlesource.com/chromium/src/+/main/docs/gpu/swiftshader.md - '--window-size=1920,1080', // https://source.chromium.org/search?q=lang:cpp+symbol:kWindowSize&ss=chromium + "--allow-running-insecure-content", // https://source.chromium.org/search?q=lang:cpp+symbol:kAllowRunningInsecureContent&ss=chromium + "--autoplay-policy=user-gesture-required", // https://source.chromium.org/search?q=lang:cpp+symbol:kAutoplayPolicy&ss=chromium + "--disable-background-timer-throttling", + "--disable-component-update", // https://source.chromium.org/search?q=lang:cpp+symbol:kDisableComponentUpdate&ss=chromium + "--disable-domain-reliability", // https://source.chromium.org/search?q=lang:cpp+symbol:kDisableDomainReliability&ss=chromium + "--disable-features=AudioServiceOutOfProcess,IsolateOrigins,site-per-process", // https://source.chromium.org/search?q=file:content_features.cc&ss=chromium + "--disable-ipc-flooding-protection", + "--disable-print-preview", // https://source.chromium.org/search?q=lang:cpp+symbol:kDisablePrintPreview&ss=chromium + "--disable-dev-shm-usage", + "--disable-setuid-sandbox", // https://source.chromium.org/search?q=lang:cpp+symbol:kDisableSetuidSandbox&ss=chromium + "--disable-site-isolation-trials", // https://source.chromium.org/search?q=lang:cpp+symbol:kDisableSiteIsolation&ss=chromium + "--disable-speech-api", // https://source.chromium.org/search?q=lang:cpp+symbol:kDisableSpeechAPI&ss=chromium + "--disable-web-security", // https://source.chromium.org/search?q=lang:cpp+symbol:kDisableWebSecurity&ss=chromium + "--disk-cache-size=33554432", // https://source.chromium.org/search?q=lang:cpp+symbol:kDiskCacheSize&ss=chromium + "--enable-features=SharedArrayBuffer", // https://source.chromium.org/search?q=file:content_features.cc&ss=chromium + "--hide-scrollbars", // https://source.chromium.org/search?q=lang:cpp+symbol:kHideScrollbars&ss=chromium + "--ignore-gpu-blocklist", // https://source.chromium.org/search?q=lang:cpp+symbol:kIgnoreGpuBlocklist&ss=chromium + "--in-process-gpu", // https://source.chromium.org/search?q=lang:cpp+symbol:kInProcessGPU&ss=chromium + "--mute-audio", // https://source.chromium.org/search?q=lang:cpp+symbol:kMuteAudio&ss=chromium + "--no-default-browser-check", // https://source.chromium.org/search?q=lang:cpp+symbol:kNoDefaultBrowserCheck&ss=chromium + "--no-first-run", + "--no-pings", // https://source.chromium.org/search?q=lang:cpp+symbol:kNoPings&ss=chromium + "--no-sandbox", // https://source.chromium.org/search?q=lang:cpp+symbol:kNoSandbox&ss=chromium + "--no-zygote", // https://source.chromium.org/search?q=lang:cpp+symbol:kNoZygote&ss=chromium + "--use-gl=angle", // https://chromium.googlesource.com/chromium/src/+/main/docs/gpu/swiftshader.md + "--use-angle=swiftshader", // https://chromium.googlesource.com/chromium/src/+/main/docs/gpu/swiftshader.md + "--window-size=1920,1080", // https://source.chromium.org/search?q=lang:cpp+symbol:kWindowSize&ss=chromium ]; if (Chromium.headless === true) { - result.push('--single-process'); // https://source.chromium.org/search?q=lang:cpp+symbol:kSingleProcess&ss=chromium + result.push("--single-process"); // https://source.chromium.org/search?q=lang:cpp+symbol:kSingleProcess&ss=chromium } else { - result.push('--start-maximized'); // https://source.chromium.org/search?q=lang:cpp+symbol:kStartMaximized&ss=chromium + result.push("--start-maximized"); // https://source.chromium.org/search?q=lang:cpp+symbol:kStartMaximized&ss=chromium } return result; @@ -187,8 +204,8 @@ class Chromium { /** * If the `chromium` binary already exists in /tmp/chromium, return it. */ - if (existsSync('/tmp/chromium') === true) { - return Promise.resolve('/tmp/chromium'); + if (existsSync("/tmp/chromium") === true) { + return Promise.resolve("/tmp/chromium"); } if (input && isValidUrl(input)) { @@ -199,7 +216,7 @@ class Chromium { * otherwise, the default location is ../bin. * A custom location is needed for workflows that using custom packaging. */ - input ??= join(__dirname, '..', 'bin'); + input ??= join(__dirname, "..", "bin"); /** * If the input directory doesn't exist, throw an error. @@ -213,7 +230,12 @@ class Chromium { LambdaFS.inflate(`${input}/swiftshader.tar.br`), ]; - if (process.env.AWS_EXECUTION_ENV !== undefined && /^AWS_Lambda_nodejs(?:14|16|18)[.]x$/.test(process.env.AWS_EXECUTION_ENV) === true) { + if ( + process.env.AWS_EXECUTION_ENV !== undefined && + /^AWS_Lambda_nodejs(?:14|16|18)[.]x$/.test( + process.env.AWS_EXECUTION_ENV + ) === true + ) { promises.push(LambdaFS.inflate(`${input}/aws.tar.br`)); } @@ -227,17 +249,20 @@ class Chromium { * False is returned if Serverless environment variables `IS_LOCAL` or `IS_OFFLINE` are set. */ static get headless() { - if (process.env.IS_LOCAL !== undefined || process.env.IS_OFFLINE !== undefined) { + if ( + process.env.IS_LOCAL !== undefined || + process.env.IS_OFFLINE !== undefined + ) { return false; } if (process.env.NODE_ENV === "test") { return true; } const environments = [ - 'AWS_LAMBDA_FUNCTION_NAME', - 'FUNCTION_NAME', - 'FUNCTION_TARGET', - 'FUNCTIONS_EMULATOR', + "AWS_LAMBDA_FUNCTION_NAME", + "FUNCTION_NAME", + "FUNCTION_TARGET", + "FUNCTIONS_EMULATOR", ]; return environments.some((key) => process.env[key] !== undefined); diff --git a/source/lambdafs.ts b/source/lambdafs.ts index de95eab..57a63f8 100644 --- a/source/lambdafs.ts +++ b/source/lambdafs.ts @@ -1,8 +1,8 @@ -import { createReadStream, createWriteStream, existsSync } from 'node:fs'; -import { tmpdir } from 'node:os'; -import { basename, join } from 'node:path'; -import { extract } from 'tar-fs'; -import { createBrotliDecompress, createUnzip } from 'node:zlib'; +import { createReadStream, createWriteStream, existsSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { basename, join } from "node:path"; +import { extract } from "tar-fs"; +import { createBrotliDecompress, createUnzip } from "node:zlib"; class LambdaFS { /** @@ -11,7 +11,15 @@ class LambdaFS { * @param filePath Path of the file to decompress. */ static inflate(filePath: string): Promise { - const output = filePath.includes("swiftshader") ? tmpdir() : join(tmpdir(), basename(filePath).replace(/[.](?:t(?:ar(?:[.](?:br|gz))?|br|gz)|br|gz)$/i, '')); + const output = filePath.includes("swiftshader") + ? tmpdir() + : join( + tmpdir(), + basename(filePath).replace( + /[.](?:t(?:ar(?:[.](?:br|gz))?|br|gz)|br|gz)$/i, + "" + ) + ); return new Promise((resolve, reject) => { if (filePath.includes("swiftshader")) { @@ -30,27 +38,33 @@ class LambdaFS { if (/[.](?:t(?:ar(?:[.](?:br|gz))?|br|gz))$/i.test(filePath) === true) { target = extract(output); - target.once('finish', () => { + target.once("finish", () => { return resolve(output); }); } else { target = createWriteStream(output, { mode: 0o700 }); } - source.once('error', (error: Error) => { + source.once("error", (error: Error) => { return reject(error); }); - target.once('error', (error: Error) => { + target.once("error", (error: Error) => { return reject(error); }); - target.once('close', () => { + target.once("close", () => { return resolve(output); }); if (/(?:br|gz)$/i.test(filePath) === true) { - source.pipe(/br$/i.test(filePath) ? createBrotliDecompress({ chunkSize: 2 ** 21 }) : createUnzip({ chunkSize: 2 ** 21 })).pipe(target); + source + .pipe( + /br$/i.test(filePath) + ? createBrotliDecompress({ chunkSize: 2 ** 21 }) + : createUnzip({ chunkSize: 2 ** 21 }) + ) + .pipe(target); } else { source.pipe(target); }