Compare commits

..

No commits in common. "master" and "v110.0.1" have entirely different histories.

43 changed files with 758 additions and 1112 deletions

View File

@ -12,11 +12,10 @@ For Playwright-specific bugs, please refer to: https://github.com/microsoft/play
-->
## Environment
- `chromium` Version:
- `puppeteer` / `puppeteer-core` Version:
- Node.js Version: <!-- 16.x | 18.x -->
- Lambda / GCF Runtime: <!-- `nodejs16` | `nodejs18.x` -->
* `chromium` Version:
* `puppeteer` / `puppeteer-core` Version:
* Node.js Version: <!-- 14.x | 16.x -->
* Lambda / GCF Runtime: <!-- `nodejs8.10` | `nodejs10.x` | `nodejs12.x` -->
## Expected Behavior

View File

@ -12,12 +12,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v3
with:
node-version: 20.x
node-version: 18.x
- name: Install Packages
run: npm ci
@ -29,7 +29,7 @@ jobs:
run: make chromium.zip
- name: Upload Layer Artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3
with:
name: chromium
path: chromium.zip
@ -43,23 +43,23 @@ jobs:
event:
- example.com
version:
- 14
- 16
- 18
- 20
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Setup Python
uses: actions/setup-python@v5
uses: actions/setup-python@v4
with:
python-version: "3.x"
python-version: '3.x'
- name: Setup AWS SAM CLI
uses: aws-actions/setup-sam@v2
- name: Download Layer Artifact
uses: actions/download-artifact@v4
uses: actions/download-artifact@v3
with:
name: chromium

View File

@ -10,15 +10,16 @@ jobs:
name: Build and release
runs-on: ubuntu-latest
steps:
# Install jq so I can edit package.json from the command line
- run: sudo apt-get install jq -y
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v3
with:
node-version: 20.x
node-version: 18.x
registry-url: https://registry.npmjs.org/
- run: npm ci
@ -58,7 +59,7 @@ jobs:
cd ..
- name: Upload items to Github Release
uses: ncipollo/release-action@v1.14.0
uses: ncipollo/release-action@v1
with:
tag: ${{ github.ref_name }}
body: |
@ -73,7 +74,6 @@ jobs:
artifacts: "chromium-${{ github.ref_name }}-layer.zip,chromium-${{ github.ref_name }}-pack.tar"
prerelease: false
draft: true
generateReleaseNotes: true
token: ${{ github.token }}
owner: Sparticuz
repo: chromium

1
.gitignore vendored
View File

@ -12,4 +12,3 @@ _/amazon/.aws-sam
*.tgz
examples/**/package-lock.json
examples/**/.serverless
docker

View File

@ -8,23 +8,20 @@ pretest:
npm install --prefix _/amazon/handlers puppeteer-core@latest --bin-links=false --fund=false --omit=optional --omit=dev --package-lock=false --save=false
test:
sam local invoke --template _/amazon/template.yml --event _/amazon/events/example.com.json node20
test16:
sam local invoke --template _/amazon/template.yml --event _/amazon/events/example.com.json node16
test18:
sam local invoke --template _/amazon/template.yml --event _/amazon/events/example.com.json node18
.fonts.zip:
zip -9 --filesync --move --recurse-paths .fonts.zip .fonts/
%.zip:
npm install --fund=false --package-lock=false
npm run build
mkdir -p nodejs
npm install --prefix nodejs/ tar-fs@3.0.6 follow-redirects@1.15.9 --bin-links=false --fund=false --omit=optional --omit=dev --package-lock=false --save=false
npm install --prefix nodejs/ tar-fs@2.1.1 follow-redirects@1.15.2 --bin-links=false --fund=false --omit=optional --omit=dev --package-lock=false --save=false
npm pack
mkdir -p nodejs/node_modules/@sparticuz/chromium/
tar --directory nodejs/node_modules/@sparticuz/chromium/ --extract --file sparticuz-chromium-*.tgz --strip-components=1
npx clean-modules --directory nodejs "**/*.d.ts" "**/@types/**" "**/*.@(yaml|yml)" --yes
npx clean-modules --directory nodejs --include "**/*.d.ts" "**/@types/**" "**/*.@(yaml|yml)" --yes
rm sparticuz-chromium-*.tgz
mkdir -p $(dir $@)
zip -9 --filesync --move --recurse-paths $@ nodejs

207
README.md
View File

@ -1,21 +1,24 @@
# @sparticuz/chromium
[![@sparticuz/chromium](https://img.shields.io/npm/v/@sparticuz/chromium.svg?style=for-the-badge)](https://www.npmjs.com/package/@sparticuz/chromium)
[![Chromium](https://img.shields.io/github/size/sparticuz/chromium/bin/chromium.br?label=Chromium&style=for-the-badge)](bin/)
[![npm](https://img.shields.io/npm/dw/@sparticuz/chromium?label=%40sparticuz%2Fchromium&style=for-the-badge)](https://www.npmjs.com/package/@sparticuz/chromium)
[![npm](https://img.shields.io/npm/dw/@sparticuz/chromium-min?label=%40sparticuz%2Fchromium-min&style=for-the-badge)](https://www.npmjs.com/package/@sparticuz/chromium-min)
[![TypeScript](https://img.shields.io/npm/types/chrome-aws-lambda?style=for-the-badge)](https://www.typescriptlang.org/dt/search?search=chromium)
[![Chromium](https://img.shields.io/badge/chromium-51_MB-brightgreen.svg?style=for-the-badge)](bin/)
[![Donate](https://img.shields.io/badge/donate-paypal-orange.svg?style=for-the-badge)](https://paypal.me/sparticuz)
## Chromium for Serverless platforms
[sparticuz/chrome-aws-lambda](https://github.com/sparticuz/chrome-aws-lambda) was originally forked from [alixaxel/chrome-aws-lambda#264](https://github.com/alixaxel/chrome-aws-lambda/pull/264).
The biggest difference, besides the chromium version, is the inclusion of some code from https://github.com/alixaxel/lambdafs, as well as dropping that as a dependency. Due to some changes in WebGL, the files in bin/swiftshader.tar.br need to be extracted to `/tmp` instead of `/tmp/swiftshader`. This necessitated changes in lambdafs.
This package was originally forked from [alixaxel/chrome-aws-lambda#264](https://github.com/alixaxel/chrome-aws-lambda/pull/264).
The biggest difference, besides the chromium version, is the inclusion of some code from https://github.com/alixaxel/lambdafs,
as well as dropping that as a dependency. Due to some changes in WebGL, the files in bin/swiftshader.tar.br need to
be extracted to `/tmp` instead of `/tmp/swiftshader`. This necessitated changes in lambdafs.
However, it quickly became difficult to maintain because of the pace of `puppeteer` updates. This package, `@sparticuz/chromium`, is not chained to `puppeteer` versions, but also does not include the overrides and hooks that the original package contained. It is only `chromium`, as well as the special code needed to decompress the brotli package, and a set of predefined arguments tailored to serverless usage.
However, it quickly became difficult to maintain because of the pace of `puppeteer` updates. This package, `@sparticuz/chromium`,
is not chained to `puppeteer` versions, but also does not include the overrides and hooks that the original package contained. It is only `chromium`, as well as the special code needed to decompress the brotli package.
## Install
[`puppeteer` ships with a preferred version of `chromium`](https://pptr.dev/faq/#q-why-doesnt-puppeteer-vxxx-work-with-chromium-vyyy). In order to figure out what version of `@sparticuz/chromium` you will need, please visit [Puppeteer's Chromium Support page](https://pptr.dev/chromium-support).
[`puppeteer` ships with a prefered version of `chromium`](https://pptr.dev/faq/#q-why-doesnt-puppeteer-vxxx-work-with-chromium-vyyy).
In order to figure out what version of `@sparticuz/chromium` you will need, please visit [Puppeteer's Chromium Support page](https://pptr.dev/chromium-support).
> For example, as of today, the latest version of `puppeteer` is `18.0.5`. The latest version of `chromium` stated on `puppeteer`'s support page is `106.0.5249.0`. So you need to install `@sparticuz/chromium@106`.
@ -26,8 +29,7 @@ npm install --save puppeteer-core@$PUPPETEER_VERSION
npm install --save-dev @sparticuz/chromium@$CHROMIUM_VERSION
```
If your vendor does not allow large deploys (`chromium.br` is 50+ MB), you'll need to host the `chromium-v#-pack.tar` separately and use the [`@sparticuz/chromium-min` package](https://github.com/Sparticuz/chromium#-min-package).
If your vendor does not allow large deploys (`chromium.br` is 50+ MB), you'll need to host the `chromium-v#-pack.tar` separatly and use the [`@sparticuz/chromium-min` package](https://github.com/Sparticuz/chromium#-min-package).
```shell
npm install --save @sparticuz/chromium-min@$CHROMIUM_VERSION
```
@ -39,8 +41,6 @@ If you wish to install an older version of Chromium, take a look at [@sparticuz/
The @sparticuz/chromium version schema is as follows:
`MajorChromiumVersion.MinorChromiumIncrement.@Sparticuz/chromiumPatchLevel`
Because this package follows Chromium's releases, it does NOT follow semantic versioning. **Breaking changes can occur with the 'patch' level.** Please check the release notes for information on breaking changes.
## Usage
This package works with all the currently supported AWS Lambda Node.js runtimes out of the box.
@ -50,25 +50,13 @@ const test = require("node:test");
const puppeteer = require("puppeteer-core");
const chromium = require("@sparticuz/chromium");
// Optional: If you'd like to use the new headless mode. "shell" is the default.
// NOTE: Because we build the shell binary, this option does not work.
// However, this option will stay so when we migrate to full chromium it will work.
chromium.setHeadlessMode = true;
// Optional: If you'd like to disable webgl, true is the default.
chromium.setGraphicsMode = false;
// Optional: Load any fonts you need. Open Sans is included by default in AWS Lambda instances
await chromium.font(
"https://raw.githack.com/googlei18n/noto-emoji/master/fonts/NotoColorEmoji.ttf"
);
test("Check the page title of example.com", async (t) => {
const browser = await puppeteer.launch({
args: chromium.args,
defaultViewport: chromium.defaultViewport,
executablePath: await chromium.executablePath(),
headless: chromium.headless,
ignoreHTTPSErrors: true,
});
const page = await browser.newPage();
@ -85,8 +73,8 @@ test("Check the page title of example.com", async (t) => {
```javascript
const test = require("node:test");
// Need to rename playwright's chromium object to something else
const { chromium: playwright } = require("playwright-core");
const chromium = require("@sparticuz/chromium");
const { chromium: playwright } = require('playwright-core');
const chromium = require('@sparticuz/chromium');
test("Check the page title of example.com", async (t) => {
const browser = await playwright.launch({
@ -104,19 +92,18 @@ test("Check the page title of example.com", async (t) => {
assert.strictEqual(pageTitle, "Example Domain");
});
```
You should allocate at least 512 MB of RAM to your instance, however 1600 MB (or more) is recommended.
You should allocate at least 512 MB of RAM to your Lambda, however 1600 MB (or more) is recommended.
### -min package
The -min package DOES NOT include the chromium brotli files. There are a few instances where this is useful. Primarily, this is useful when your host has file size limits.
The -min package DOES NOT include the chromium brotli files. There are a few instances where this
is useful. Primarily, this is useful when you have file size limits.
To use the -min package please install the `@sparticuz/chromium-min` package.
When using the -min package, you need to specify the location of the brotli files.
In this example, /opt/chromium contains all the brotli files
```
/opt
/chromium
@ -124,19 +111,22 @@ In this example, /opt/chromium contains all the brotli files
/chromium.br
/swiftshader.tar.br
```
```javascript
const browser = await puppeteer.launch({
args: chromium.args,
defaultViewport: chromium.defaultViewport,
executablePath: await chromium.executablePath("/opt/chromium"),
headless: chromium.headless,
ignoreHTTPSErrors: true,
});
```
In the following example, https://www.example.com/chromiumPack.tar contains all the brotli files.
Generally, this would be a location on S3, or another very fast downloadable location,
that is close to your function's execution location.
In the following example, https://www.example.com/chromiumPack.tar contains all the brotli files. Generally, this would be a location on S3, or another very fast downloadable location, that is in close proximity to your function's execution location.
On the initial iteration, `@sparticuz/chromium` will download the pack tar file, untar the files to `/tmp/chromium-pack`, then will un-brotli the `chromium` binary to `/tmp/chromium`. The following iterations will see that `/tmp/chromium` exists and will use the already downloaded files.
@sparticuz/chromium will download the pack tar file, untar the files to /tmp/chromium-pack,
then will un-brotli the files to /tmp/chromium. The next iteration will have /tmp/chromium exist
and will use the already downloaded files.
The latest chromium-pack.tar file will be on the latest [release](https://github.com/Sparticuz/chromium/releases).
@ -144,128 +134,42 @@ The latest chromium-pack.tar file will be on the latest [release](https://github
const browser = await puppeteer.launch({
args: chromium.args,
defaultViewport: chromium.defaultViewport,
executablePath: await chromium.executablePath(
"https://www.example.com/chromiumPack.tar"
),
executablePath: await chromium.executablePath("https://www.example.com/chromiumPack.tar"),
headless: chromium.headless,
ignoreHTTPSErrors: true,
});
```
### Examples
### Running Locally
Here are some example projects and help with other services
This package will run in headless mode when `NODE_ENV = "test"`. If you want to run using your own local binary, set `IS_LOCAL` to anything.
- [Production Dependency](https://github.com/Sparticuz/chromium/tree/master/examples/production-dependency)
- [Serverless Framework with Lambda Layer](https://github.com/Sparticuz/chromium/tree/master/examples/serverless-with-lambda-layer)
- [Serverless Framework with Pre-existing Lambda Layer](https://github.com/Sparticuz/chromium/tree/master/examples/serverless-with-preexisting-lambda-layer)
- [Chromium-min](https://github.com/Sparticuz/chromium/tree/master/examples/remote-min-binary)
- [AWS SAM](https://github.com/Sparticuz/chromium/tree/master/examples/aws-sam)
- [Webpack](https://github.com/Sparticuz/chromium/issues/24#issuecomment-1343196897)
- [Netlify](https://github.com/Sparticuz/chromium/issues/24#issuecomment-1414107620)
## API
### Running Locally & Headless/Headful mode
This version of `chromium` is built using the `headless.gn` build variables, which does not appear to even include a GUI. [Also, at this point, AWS Lambda 2 does not support a modern version of `glibc`](https://github.com/aws/aws-lambda-base-images/issues/59), so this package does not include an ARM version yet, which means it will not work on any M Series Apple products. If you need to test your code using a headful or ARM version, please use your locally installed version of `chromium/chrome`, or you may use the `puppeteer` provided version. Users have reported installing `rosetta` on MacOS will also work.
```shell
npx @puppeteer/browsers install chromium@latest --path /tmp/localChromium
```
For more information on installing a specific version of `chromium`, checkout [@puppeteer/browsers](https://www.npmjs.com/package/@puppeteer/browsers).
For example, you can set your code to use an ENV variable such as `IS_LOCAL`, then use if/else statements to direct puppeteer to the correct environment.
```javascript
const browser = await puppeteer.launch({
args: process.env.IS_LOCAL ? puppeteer.defaultArgs() : chromium.args,
defaultViewport: chromium.defaultViewport,
executablePath: process.env.IS_LOCAL
? "/tmp/localChromium/chromium/linux-1122391/chrome-linux/chrome"
: await chromium.executablePath(),
headless: process.env.IS_LOCAL ? false : chromium.headless,
});
```
## Frequently asked questions
### Can I use ARM or Graviton instances?
Amazon's default Lambda base image is quite old at this point and does not support newer versions of `glibc` that chromium requires. When Amazon Linux 2023 comes to Lambda as the default base image, ARM support should be possible. Ref: https://github.com/Sparticuz/chrome-aws-lambda/pull/11, https://github.com/aws/aws-lambda-base-images/issues/59
### Can I use Google Chrome or Chrome for Testing, what is headless_shell?
`headless_shell` is a purpose built version of `chromium` specific for headless purposes. It does not include the GUI at all and only works via remote debugging connection. Ref: https://chromium.googlesource.com/chromium/src/+/lkgr/headless/README.md, https://source.chromium.org/chromium/chromium/src/+/main:headless/app/headless_shell.cc
### Can I use the "new" Headless mode?
From what I can tell, `headless_shell` does not seem to include support for the "new" headless mode.
### It doesn't work with Webpack!?!
Try marking this package as an external. Ref: https://webpack.js.org/configuration/externals/
### I'm experiencing timeouts or failures closing Chromium
This is a common issue. Chromium sometimes opens up more pages than you ask for. You can try the following
```typescript
for (const page of await browser.pages()) {
await page.close();
}
await browser.close();
```
You can also try the following if one of the calls is hanging for some reason.
```typescript
await Promise.race([browser.close(), browser.close(), browser.close()]);
```
Always `await browser.close()`, even if your script is returning an error.
### I need Accessible pdf files
This is due to the way @sparticuz/chromium is built. If you require accessible pdf's, you'll need to
recompile chromium yourself with the following patch. You can then use that binary with @sparticuz/chromium-min.
_Note_: This will increase the time required to generate a PDF.
```patch
diff --git a/_/ansible/plays/chromium.yml b/_/ansible/plays/chromium.yml
index b42c740..49111d7 100644
--- a/_/ansible/plays/chromium.yml
+++ b/_/ansible/plays/chromium.yml
@@ -249,8 +249,9 @@
blink_symbol_level = 0
dcheck_always_on = false
disable_histogram_support = false
- enable_basic_print_dialog = false
enable_basic_printing = true
+ enable_pdf = true
+ enable_tagged_pdf = true
enable_keystone_registration_framework = false
enable_linux_installer = false
enable_media_remoting = false
```
| Method / Property | Returns | Description |
| --------------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `font(url)` | `{?Promise<string>}` | Provisions a custom font and returns its basename. |
| `args` | `{!Array<string>}` | Provides a list of recommended additional [Chromium flags](https://github.com/GoogleChrome/chrome-launcher/blob/master/docs/chrome-flags-for-tools.md). |
| `defaultViewport` | `{!Object}` | Returns more sensible default viewport settings. |
| `executablePath(location)` | `{?Promise<string>}` | Returns the path the Chromium binary was extracted to. |
| `headless` | `{!boolean}` | Returns `true` if we are running on AWS Lambda or GCF. |
## Fonts
The Amazon Linux 2 AWS Lambda runtime is not provisioned with any font faces.
The Amazon Linux 2 AWS Lambda runtime is no longer provisioned with any font faces.
Because of this, this package ships with [Open Sans](https://fonts.google.com/specimen/Open+Sans), which supports the following scripts:
- Latin
- Greek
- Cyrillic
* Latin
* Greek
* Cyrillic
To provision additional fonts, simply call the `font()` method with an absolute path or URL:
```typescript
await chromium.font("/var/task/fonts/NotoColorEmoji.ttf");
await chromium.font('/var/task/fonts/NotoColorEmoji.ttf');
// or
await chromium.font(
"https://raw.githack.com/googlei18n/noto-emoji/master/fonts/NotoColorEmoji.ttf"
);
await chromium.font('https://raw.githack.com/googlei18n/noto-emoji/master/fonts/NotoColorEmoji.ttf');
```
> `Noto Color Emoji` (or similar) is needed if you want to [render emojis](https://getemoji.com/).
@ -274,11 +178,13 @@ await chromium.font(
This method should be invoked _before_ launching Chromium.
> On non-serverless environments, the `font()` method is a no-op to avoid polluting the user space.
---
Alternatively, it's also possible to provision fonts via AWS Lambda Layers.
Simply create a directory named `.fonts` or `fonts` and place any font faces you want there:
Simply create a directory named `.fonts` and place any font faces you want there:
```
.fonts
@ -289,26 +195,8 @@ Simply create a directory named `.fonts` or `fonts` and place any font faces you
Afterwards, you just need to ZIP the directory and upload it as a AWS Lambda Layer:
```shell
zip -9 --filesync --move --recurse-paths fonts.zip fonts/
zip -9 --filesync --move --recurse-paths .fonts.zip .fonts/
```
## Graphics
By default, this package uses `swiftshader`/`angle` to do CPU acceleration for WebGL. This is the only known way to enable WebGL on a serverless platform. You can disable WebGL by setting `chromium.setGraphiceMode = false;` _before_ launching Chromium. Disabling this will also skip the extract of the `bin/swiftshader.tar.br` file, which saves about a second of initial execution time. Disabling graphics is recommended if you know you are not using any WebGL.
## API
| Method / Property | Returns | Description |
| ----------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `font(url)` | `Promise<string>` | Provisions a custom font and returns its basename. |
| `args` | `Array<string>` | Provides a list of recommended additional [Chromium flags](https://github.com/GoogleChrome/chrome-launcher/blob/master/docs/chrome-flags-for-tools.md). |
| `defaultViewport` | `Object` | Returns a sensible default viewport for serverless. |
| `executablePath(location?: string)` | `Promise<string>` | Returns the path the Chromium binary was extracted to. |
| `setHeadlessMode` | `void` | Sets the headless mode to either `true` or `"shell"` |
| `headless` | `true \| "shell"` | Returns `true` or `"shell"` depending on what version of chrome's headless you are running |
| `setGraphicsMode` | `void` | Sets the graphics mode to either `true` or `false` |
| `graphics` | `boolean` | Returns a boolean depending on whether webgl is enabled or disabled |
## Compiling
To compile your own version of Chromium check the [Ansible playbook instructions](_/ansible).
@ -326,7 +214,6 @@ make chromium.zip
```
The above will create a `chromium.zip` file, which can be uploaded to your Layers console. You can and should upload using the `aws cli`. (Replace the variables with your own values)
```shell
bucketName="chromiumUploadBucket" && \
versionNumber="107" && \
@ -344,7 +231,6 @@ According to our benchmarks, it's 40% to 50% faster than using the off-the-shelf
- Add the import or require for `puppeteer-core`
- Change the browser launch to use the native `puppeteer.launch()` function
- Change the `executablePath` to be a function.
```diff
-const chromium = require('@sparticuz/chrome-aws-lambda');
+const chromium = require("@sparticuz/chromium");
@ -381,7 +267,6 @@ exports.handler = async (event, context, callback) => {
return callback(null, result);
};
```
## Compression
The Chromium binary is compressed using the Brotli algorithm.

View File

@ -3,14 +3,21 @@
"url": "https://example.com",
"expected": {
"title": "Example Domain",
"screenshot": "e610a8be5568f23c453b08928460aae3ae0b4b0a"
"screenshot": "02b4ebddcf15459571f775df95174eaf08b4fa45"
}
},
{
"url": "https://example.com",
"expected": {
"title": "Example Domain",
"screenshot": "02b4ebddcf15459571f775df95174eaf08b4fa45"
}
},
{
"url": "https://get.webgl.org",
"expected": {
"remove": "logo-container",
"screenshot": "ec6c79a571b4cb5727c6fc23f9da30de3868138c"
"screenshot": "f4577bc1eef0f4625985a439914c600dcb995e71"
}
}
]

View File

@ -1,58 +1,47 @@
const { ok } = require("node:assert");
const { createHash } = require("node:crypto");
const { ok } = require('assert');
const { createHash } = require('crypto');
const puppeteer = require("puppeteer-core");
const chromium = require("@sparticuz/chromium");
const chromium = require('@sparticuz/chromium');
exports.handler = async (event, context) => {
let browser = null;
try {
browser = await puppeteer.launch({
const browser = await puppeteer.launch({
args: chromium.args,
defaultViewport: chromium.defaultViewport,
dumpio: true,
executablePath: await chromium.executablePath(),
headless: chromium.headless,
acceptInsecureCerts: true,
ignoreHTTPSErrors: true,
});
console.log("Chromium version", await browser.version());
const contexts = [
browser.defaultBrowserContext(),
];
for (let job of event) {
const page = await browser.newPage();
if (job.hasOwnProperty("url") === true) {
await page.goto(job.url, { waitUntil: ["domcontentloaded", "load"] });
if (job.hasOwnProperty("expected") === true) {
if (job.expected.hasOwnProperty("title") === true) {
ok(
(await page.title()) === job.expected.title,
`Title assertion failed.`
);
while (contexts.length < event.length) {
contexts.push(await browser.createIncognitoBrowserContext());
}
if (job.expected.hasOwnProperty("screenshot") === true) {
if (job.expected.hasOwnProperty("remove") === true) {
for (let context of contexts) {
const job = event.shift();
const page = await context.newPage();
if (job.hasOwnProperty('url') === true) {
await page.goto(job.url, { waitUntil: ['domcontentloaded', 'load'] });
if (job.hasOwnProperty('expected') === true) {
if (job.expected.hasOwnProperty('title') === true) {
ok(await page.title() === job.expected.title, `Title assertion failed.`);
}
if (job.expected.hasOwnProperty('screenshot') === true) {
if (job.expected.hasOwnProperty('remove') === true ) {
await page.evaluate((selector) => {
document.getElementById(selector).remove();
}, job.expected.remove);
}
const screenshot = Buffer.from(await page.screenshot());
/*
console.log(
`data:image/png;base64,${screenshot.toString("base64")}`,
createHash("sha1")
.update(screenshot.toString("base64"))
.digest("hex")
);
*/
ok(
createHash("sha1")
.update(screenshot.toString("base64"))
.digest("hex") === job.expected.screenshot,
`Screenshot assertion failed.`
);
ok(createHash('sha1').update((await page.screenshot()).toString('base64')).digest('hex') === job.expected.screenshot, `Screenshot assertion failed.`);
}
}
}
@ -61,9 +50,6 @@ exports.handler = async (event, context) => {
throw error.message;
} finally {
if (browser !== null) {
for (const page of await browser.pages()) {
await page.close();
}
await browser.close();
}
}

View File

@ -12,10 +12,21 @@ Resources:
LayerName: sparticuz-chromium
ContentUri: code/
CompatibleRuntimes:
- nodejs14.x
- nodejs16.x
- nodejs18.x
- nodejs20.x
node14:
Type: AWS::Serverless::Function
Properties:
Layers:
- !Ref layer
Handler: handlers/index.handler
Runtime: nodejs14.x
Policies:
- AWSLambdaBasicExecutionRole
- AWSXRayDaemonWriteAccess
Tracing: Active
node16:
Type: AWS::Serverless::Function
Properties:
@ -38,14 +49,3 @@ Resources:
- AWSLambdaBasicExecutionRole
- AWSXRayDaemonWriteAccess
Tracing: Active
node20:
Type: AWS::Serverless::Function
Properties:
Layers:
- !Ref layer
Handler: handlers/index.handler
Runtime: nodejs20.x
Policies:
- AWSLambdaBasicExecutionRole
- AWSXRayDaemonWriteAccess
Tracing: Active

0
_/ansible/ansible.cfg Normal file → Executable file
View File

View File

@ -4,9 +4,9 @@
[localhost:vars]
ansible_connection=local
ansible_python_interpreter=python
image=ami-06c68f701d8090592
image=ami-08d090f841c8435e9
region=us-east-1
instance_size=c7i.12xlarge
instance_size=c6i.12xlarge
[aws]
@ -14,4 +14,4 @@ instance_size=c7i.12xlarge
ansible_connection=ssh
ansible_python_interpreter=auto_silent
ansible_ssh_private_key_file=ansible.pem
chromium_revision=1343869
chromium_revision=1083080

View File

@ -58,7 +58,7 @@
ebs:
delete_on_termination: true
volume_type: io2
volume_size: 256
volume_size: 128
iops: 3000
register: ec2
@ -71,7 +71,7 @@
wait_for:
host: "{{ ec2.instances[0].public_ip_address }}"
port: 22
timeout: 320
timeout: 240
state: started
- name: AWS
@ -142,7 +142,8 @@
- name: Checking for Directory Structure
stat:
path: /srv/source/chromium
register: structure
register:
structure
- name: Creating Directory Structure
become: true
@ -171,7 +172,7 @@
dest: /srv/source/chromium/.gclient
owner: ec2-user
group: ec2-user
mode: "0664"
mode: '0664'
- name: Checking for Chromium
stat:
@ -210,22 +211,22 @@
backrefs: yes
with_items:
- {
path: "sandbox_ipc_linux.cc",
path: 'sandbox_ipc_linux.cc',
line: '\1PLOG(WARNING) << "poll"; failed_polls = 0;',
regexp: '^(\s+)PLOG[(]WARNING[)] << "poll";$',
}
- {
path: "renderer_host/render_process_host_impl.cc",
path: 'renderer_host/render_process_host_impl.cc',
line: '\1// \2\3',
regexp: '^( )(\s*)(CHECK[(]render_process_host->InSameStoragePartition[(])$',
}
- {
path: "renderer_host/render_process_host_impl.cc",
path: 'renderer_host/render_process_host_impl.cc',
line: '\1// \2\3',
regexp: '^( )(\s*)(browser_context->GetStoragePartition[(]site_instance,)$',
}
- {
path: "renderer_host/render_process_host_impl.cc",
path: 'renderer_host/render_process_host_impl.cc',
line: '\1// \2\3',
regexp: '^( )(\s*)(false /[*] can_create [*]/[)][)][)];)$',
}
@ -236,6 +237,12 @@
path: /srv/source/chromium/src/out/Headless
state: directory
- name: Mounting Build Directory in Memory
become: true
become_user: root
shell: |
mount --types tmpfs --options size=24G,nr_inodes=128k,mode=1777 tmpfs /srv/source/chromium/src/out/Headless
- name: Creating Headless Chromium Configuration
copy:
content: |
@ -333,7 +340,7 @@
amazon.aws.ec2_instance:
wait: yes
state: absent
instance_ids: "{{ ec2.instance_ids }}"
instance_ids: '{{ ec2.instance_ids }}'
region: "{{ region }}"
- name: Deleting Security Group

Binary file not shown.

Binary file not shown.

BIN
bin/aws.tar.br Executable file

Binary file not shown.

BIN
bin/chromium.br Normal file → Executable file

Binary file not shown.

Binary file not shown.

BIN
bin/swiftshader.tar.br Normal file → Executable file

Binary file not shown.

View File

@ -1 +0,0 @@
.aws-sam

View File

@ -1,19 +0,0 @@
# Chromium as a Layer for AWS SAM
1. Install AWS SAM CLI: https://github.com/aws/aws-sam-cli/
1. Ensure Docker is installed and running: https://www.docker.com/
1. Build the project:
```sh
sam build
```
1. Invoke the AWS Lambda Function locally with:
```sh
sam local invoke ExampleFunction
```
This example connects to https://www.example.com and outputs the page's title as the function result. See the source code in [`app.mjs`](functions/exampleFunction/app.mjs) for more details.

View File

@ -1,24 +0,0 @@
import chromium from '@sparticuz/chromium';
import puppeteer from 'puppeteer-core';
export const lambdaHandler = async (event, context) => {
const browser = await puppeteer.launch({
args: chromium.args,
defaultViewport: chromium.defaultViewport,
executablePath: await chromium.executablePath(),
headless: chromium.headless,
});
const page = await browser.newPage();
await page.goto("https://www.example.com", { waitUntil: "networkidle0" });
const browserVersion = await browser.version();
const pageTitle = await page.title();
await page.close();
await browser.close();
return { result: 'success', browserVersion, pageTitle };
}

View File

@ -1,13 +0,0 @@
{
"name": "ExampleFunction",
"private": true,
"version": "0.1.0",
"description": "AWS Lambda Function that loads Chromium. Refer to https://github.com/Sparticuz/chromium#install for compatible versions.",
"main": "app.mjs",
"devDependencies": {
"@sparticuz/chromium": "^119.0.0"
},
"dependencies": {
"puppeteer-core": "^21.5.1"
}
}

View File

@ -1,9 +0,0 @@
{
"name": "ChromiumLayer",
"private": true,
"version": "1.0.0",
"description": "Chromium layer for AWS Lambda",
"dependencies": {
"@sparticuz/chromium": "^119.0.0"
}
}

View File

@ -1,33 +0,0 @@
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: Example configuration for AWS SAM and Chromium
Resources:
ChromiumLayer:
Type: AWS::Serverless::LayerVersion
Properties:
Description: Chromium with Node.js integration for AWS Lambda
ContentUri: layers/chromium
CompatibleRuntimes:
- &nodejsRuntime nodejs18.x
# Chromium doesn't currently have ARM support; see https://github.com/Sparticuz/chromium#can-i-use-arm-or-graviton-instances
CompatibleArchitectures:
- &chromiumArch x86_64
RetentionPolicy: Delete
Metadata:
BuildMethod: *nodejsRuntime
BuildArchitecture: *chromiumArch
ExampleFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: functions/exampleFunction
Handler: app.lambdaHandler
Runtime: *nodejsRuntime
Architectures:
- *chromiumArch
Layers:
- !Ref ChromiumLayer
# Adjust as necessary
Timeout: 30
MemorySize: 1024

View File

@ -1,5 +1,5 @@
const puppeteer = require("puppeteer-core");
const chromium = require("@sparticuz/chromium");
const chromium = require('@sparticuz/chromium');
const handler = async () => {
try {
@ -21,9 +21,10 @@ const handler = async () => {
await page.close();
await browser.close();
} catch (error) {
throw new Error(error.message);
}
};
handler().then(() => console.log("Done"));
handler();

View File

View File

@ -1,31 +0,0 @@
const puppeteer = require("puppeteer-core");
const chromium = require("@sparticuz/chromium-min");
const handler = async () => {
try {
const browser = await puppeteer.launch({
args: chromium.args,
defaultViewport: chromium.defaultViewport,
executablePath: await chromium.executablePath(
"https://github.com/Sparticuz/chromium/releases/download/v110.0.1/chromium-v110.0.1-pack.tar"
),
headless: chromium.headless,
ignoreHTTPSErrors: true,
});
const page = await browser.newPage();
await page.goto("https://www.example.com", { waitUntil: "networkidle0" });
console.log("Chromium:", await browser.version());
console.log("Page Title:", await page.title());
await page.close();
await browser.close();
} catch (error) {
throw new Error(error.message);
}
};
handler().then(() => console.log("Done"));

View File

@ -1,17 +0,0 @@
{
"name": "chromium-remote-min",
"version": "0.0.0",
"description": "This package demonstrates using @sparticuz/chromium-min",
"license": "ISC",
"author": {
"name": "Kyle McNally"
},
"main": "index.js",
"scripts": {
"test": "node index.js"
},
"dependencies": {
"@sparticuz/chromium-min": "110.0.1",
"puppeteer-core": "19.6.3"
}
}

View File

@ -1,5 +1,5 @@
const puppeteer = require("puppeteer-core");
const chromium = require("@sparticuz/chromium");
const chromium = require('@sparticuz/chromium');
module.exports = {
handler: async () => {
@ -22,8 +22,9 @@ module.exports = {
await page.close();
await browser.close();
} catch (error) {
throw new Error(error.message);
}
},
}
};

View File

@ -1,40 +0,0 @@
## Upload Lambda layer to AWS
1. Download the layer zip from Github Releases
![Step 1](docs/step1.png)
2. Create a S3 bucket, or use a pre-existing bucket, upload the zip, and copy the URL
![Step 2](docs/step2.png)
3. Create a new layer or use a pre-existing layer. (If using a pre-existing layer, Create a new verion)
![Step 3](docs/step3.png)
4. Use the S3 file to load into to AWS Lambda Layers
![Step 4](docs/step4.png)
5. Add the layer to your serverless function
```yaml
service: sls-with-preexisting-layer
provider:
name: aws
runtime: nodejs18.x
stage: dev
region: us-east-1
timeout: 300
functions:
chromium-test:
handler: index.handler
layers:
- arn:aws:lambda:us-east-1:************:layer:chromium:*
```
# BONUS
These steps can easily be automated using the following code:
```shell
$ chromiumVersion="112.0.0"
$ bucketName="chromiumUploadBucket"
$ wget "https://github.com/Sparticuz/chromium/releases/download/v${chromiumVersion}/chromium-v${chromiumVersion}-layer.zip"
$ aws s3 cp "chromium-v${chromiumVersion}-layer.zip" "s3://${bucketName}/chromiumLayers/chromium-v${chromiumVersion}-layer.zip"
$ aws lambda publish-layer-version --layer-name chromium --description "Chromium v${chromiumVersion}" --content "S3Bucket=${bucketName},S3Key=chromiumLayers/chromium-v${chromiumVersion}-layer.zip" --compatible-runtimes nodejs --compatible-architectures x86_64
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

View File

@ -1,29 +0,0 @@
const puppeteer = require("puppeteer-core");
const chromium = require("@sparticuz/chromium");
module.exports = {
handler: async () => {
try {
const browser = await puppeteer.launch({
args: chromium.args,
defaultViewport: chromium.defaultViewport,
executablePath: await chromium.executablePath(),
headless: chromium.headless,
ignoreHTTPSErrors: true,
});
const page = await browser.newPage();
await page.goto("https://www.example.com", { waitUntil: "networkidle0" });
console.log("Chromium:", await browser.version());
console.log("Page Title:", await page.title());
await page.close();
await browser.close();
} catch (error) {
throw new Error(error.message);
}
},
};

View File

@ -1,21 +0,0 @@
{
"name": "serverless-with-lambda-layer",
"version": "0.0.0",
"description": "This package demonstrates using @sparticuz/chromium as a devDependency with a layer that contains the binaries",
"license": "ISC",
"author": {
"name": "Kyle McNally"
},
"main": "index.js",
"scripts": {
"deploy": "sls deploy",
"test": "sls invoke --function chromium-test --log"
},
"dependencies": {
"puppeteer-core": "19.6.3"
},
"devDependencies": {
"@sparticuz/chromium": "110.0.0",
"serverless": "^3.27.0"
}
}

View File

@ -1,14 +0,0 @@
service: sls-with-layer
provider:
name: aws
runtime: nodejs18.x
stage: dev
region: us-east-1
timeout: 300
functions:
chromium-test:
handler: index.handler
layers:
- arn:aws:lambda:us-east-1:************:layer:chromium:*

544
package-lock.json generated
View File

@ -1,65 +1,54 @@
{
"name": "@sparticuz/chromium",
"version": "130.0.0",
"version": "110.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@sparticuz/chromium",
"version": "130.0.0",
"version": "110.0.1",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.9",
"tar-fs": "^3.0.6"
"follow-redirects": "^1.15.2",
"tar-fs": "^2.1.1"
},
"devDependencies": {
"@tsconfig/node20": "^20.1.4",
"@tsconfig/strictest": "^2.0.5",
"@types/follow-redirects": "^1.14.4",
"@types/node": "^20.16.10",
"@types/tar-fs": "^2.0.4",
"clean-modules": "^3.1.1",
"typescript": "^5.6.2"
"@tsconfig/node14": "^1.0.3",
"@types/follow-redirects": "^1.14.1",
"@types/node": "^18.11.18",
"@types/tar-fs": "^2.0.1",
"clean-modules": "^2.0.6",
"typescript": "^4.9.4"
},
"engines": {
"node": ">= 16"
"node": ">= 14.18.0"
}
},
"node_modules/@tsconfig/node20": {
"version": "20.1.4",
"resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.4.tgz",
"integrity": "sha512-sqgsT69YFeLWf5NtJ4Xq/xAF8p4ZQHlmGW74Nu2tD4+g5fAsposc4ZfaaPixVu4y01BEiDCWLRDCvDM5JOsRxg==",
"dev": true
},
"node_modules/@tsconfig/strictest": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@tsconfig/strictest/-/strictest-2.0.5.tgz",
"integrity": "sha512-ec4tjL2Rr0pkZ5hww65c+EEPYwxOi4Ryv+0MtjeaSQRJyq322Q27eOQiFbuNgw2hpL4hB1/W/HBGk3VKS43osg==",
"node_modules/@tsconfig/node14": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
"dev": true
},
"node_modules/@types/follow-redirects": {
"version": "1.14.4",
"resolved": "https://registry.npmjs.org/@types/follow-redirects/-/follow-redirects-1.14.4.tgz",
"integrity": "sha512-GWXfsD0Jc1RWiFmMuMFCpXMzi9L7oPDVwxUnZdg89kDNnqsRfUKXEtUYtA98A6lig1WXH/CYY/fvPW9HuN5fTA==",
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@types/follow-redirects/-/follow-redirects-1.14.1.tgz",
"integrity": "sha512-THBEFwqsLuU/K62B5JRwab9NW97cFmL4Iy34NTMX0bMycQVzq2q7PKOkhfivIwxdpa/J72RppgC42vCHfwKJ0Q==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/node": {
"version": "20.16.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.10.tgz",
"integrity": "sha512-vQUKgWTjEIRFCvK6CyriPH3MZYiYlNy0fKiEYHWbcoWLEgs4opurGGKlebrTLqdSMIbXImH6XExNiIyNUv3WpA==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.19.2"
}
"version": "18.11.18",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",
"integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==",
"dev": true
},
"node_modules/@types/tar-fs": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/tar-fs/-/tar-fs-2.0.4.tgz",
"integrity": "sha512-ipPec0CjTmVDWE+QKr9cTmIIoTl7dFG/yARCM5MqK8i6CNLIG1P8x4kwDsOQY1ChZOZjH0wO9nvfgBvWl4R3kA==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/tar-fs/-/tar-fs-2.0.1.tgz",
"integrity": "sha512-qlsQyIY9sN7p221xHuXKNoMfUenOcvEBN4zI8dGsYbYCqHtTarXOEXSIgUnK+GcR0fZDse6pAIc5pIrCh9NefQ==",
"dev": true,
"dependencies": {
"@types/node": "*",
@ -75,76 +64,151 @@
"@types/node": "*"
}
},
"node_modules/b4a": {
"version": "1.6.4",
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz",
"integrity": "sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw=="
},
"node_modules/bare-events": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.2.0.tgz",
"integrity": "sha512-Yyyqff4PIFfSuthCZqLlPISTWHmnQxoPuAvkmgzsJEmG3CesdIv6Xweayl0JkCZJSB2yYIdJyEz97tpxNhgjbg==",
"optional": true
},
"node_modules/bare-fs": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.1.5.tgz",
"integrity": "sha512-5t0nlecX+N2uJqdxe9d18A98cp2u9BETelbjKpiVgQqzzmVNFYWEAjQHqS+2Khgto1vcwhik9cXucaj5ve2WWA==",
"optional": true,
"dependencies": {
"bare-events": "^2.0.0",
"bare-os": "^2.0.0",
"bare-path": "^2.0.0",
"streamx": "^2.13.0"
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/bare-os": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.2.0.tgz",
"integrity": "sha512-hD0rOPfYWOMpVirTACt4/nK8mC55La12K5fY1ij8HAdfQakD62M+H4o4tpfKzVGLgRDTuk3vjA4GqGXXCeFbag==",
"optional": true
},
"node_modules/bare-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.0.tgz",
"integrity": "sha512-DIIg7ts8bdRKwJRJrUMy/PICEaQZaPGZ26lsSx9MJSwIhSrcdHn7/C8W+XmnG/rKi6BaRcz+JO00CjZteybDtw==",
"optional": true,
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"dependencies": {
"bare-os": "^2.1.0"
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/arg": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
"dev": true
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"dependencies": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
"readable-stream": "^3.4.0"
}
},
"node_modules/buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"node_modules/chownr": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
},
"node_modules/clean-modules": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/clean-modules/-/clean-modules-3.1.1.tgz",
"integrity": "sha512-t/7dNtn6vQYxujYxdwZeLa0NsLE92KQ0XeV3CDJ2TXgLTvn3ijmjlQN0Dm9wjYQgC0miZiF66ClTQzgIeYw96A==",
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/clean-modules/-/clean-modules-2.0.6.tgz",
"integrity": "sha512-e3R40CfLfzS3tlBWO5RcIMZYsIecEERklMln5qb4HvvujyvLLTi2lPEhhCGtH5gJL+4t0lVguiynMHXNkNdoDA==",
"dev": true,
"license": "ISC",
"dependencies": {
"clipanion": "^3.2.1",
"arg": "^5.0.1",
"picomatch": "^2.3.0",
"pretty-bytes": "^6.1.0",
"pretty-ms": "^8.0.0",
"supports-color": "^9.4.0"
"pretty-bytes": "^5.6.0",
"pretty-ms": "^7.0.1",
"supports-color": "^8.1.1",
"yargs": "^17.1.1"
},
"bin": {
"clean-modules": "bin/cli.js"
},
"engines": {
"node": ">=14"
"node": ">=12"
}
},
"node_modules/clipanion": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/clipanion/-/clipanion-3.2.1.tgz",
"integrity": "sha512-dYFdjLb7y1ajfxQopN05mylEpK9ZX0sO1/RfMXdfmwjlIsPkbh4p7A682x++zFPLDCo1x3p82dtljHf5cW2LKA==",
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"dev": true,
"dependencies": {
"typanion": "^3.8.0"
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
"wrap-ansi": "^7.0.0"
},
"peerDependencies": {
"typanion": "*"
"engines": {
"node": ">=12"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
},
"node_modules/end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
@ -153,22 +217,25 @@
"once": "^1.4.0"
}
},
"node_modules/fast-fifo": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.0.tgz",
"integrity": "sha512-IgfweLvEpwyA4WgiQe9Nx6VV2QkML2NkvZnk1oKnIzXgXdWxuhF7zw4DvLTPZJn6PIUneiAXPF24QmoEqHTjyw=="
"node_modules/escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/follow-redirects": {
"version": "1.15.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
@ -178,6 +245,67 @@
}
}
},
"node_modules/fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true,
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/mkdirp-classic": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@ -187,15 +315,12 @@
}
},
"node_modules/parse-ms": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-3.0.0.tgz",
"integrity": "sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz",
"integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
"node": ">=6"
}
},
"node_modules/picomatch": {
@ -211,27 +336,27 @@
}
},
"node_modules/pretty-bytes": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz",
"integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==",
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
"integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==",
"dev": true,
"engines": {
"node": "^14.13.1 || >=16.0.0"
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/pretty-ms": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-8.0.0.tgz",
"integrity": "sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==",
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz",
"integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==",
"dev": true,
"dependencies": {
"parse-ms": "^3.0.0"
"parse-ms": "^2.1.0"
},
"engines": {
"node": ">=14.16"
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@ -246,86 +371,197 @@
"once": "^1.3.1"
}
},
"node_modules/queue-tick": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz",
"integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag=="
},
"node_modules/streamx": {
"version": "2.15.1",
"resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.1.tgz",
"integrity": "sha512-fQMzy2O/Q47rgwErk/eGeLu/roaFWV0jVsogDmrszM9uIw8L5OA+t+V93MgYlufNptfjmYR1tOMWhei/Eh7TQA==",
"node_modules/readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"dependencies": {
"fast-fifo": "^1.1.0",
"queue-tick": "^1.0.1"
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dependencies": {
"safe-buffer": "~5.2.0"
}
},
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/supports-color": {
"version": "9.4.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz",
"integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==",
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"dev": true,
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=12"
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
"node_modules/tar-fs": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz",
"integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
"integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
"dependencies": {
"chownr": "^1.1.1",
"mkdirp-classic": "^0.5.2",
"pump": "^3.0.0",
"tar-stream": "^3.1.5"
},
"optionalDependencies": {
"bare-fs": "^2.1.1",
"bare-path": "^2.1.0"
"tar-stream": "^2.1.4"
}
},
"node_modules/tar-stream": {
"version": "3.1.6",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.6.tgz",
"integrity": "sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"dependencies": {
"b4a": "^1.6.4",
"fast-fifo": "^1.2.0",
"streamx": "^2.15.0"
"bl": "^4.0.3",
"end-of-stream": "^1.4.1",
"fs-constants": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.1.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/typanion": {
"version": "3.14.0",
"resolved": "https://registry.npmjs.org/typanion/-/typanion-3.14.0.tgz",
"integrity": "sha512-ZW/lVMRabETuYCd9O9ZvMhAh8GslSqaUjxmK/JLPCh6l73CvLBiuXswj/+7LdnWOgYsQ130FqLzFz5aGT4I3Ug==",
"dev": true
},
"node_modules/typescript": {
"version": "5.6.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz",
"integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==",
"version": "4.9.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
"node": ">=4.2.0"
}
},
"node_modules/undici-types": {
"version": "6.19.6",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.6.tgz",
"integrity": "sha512-e/vggGopEfTKSvj4ihnOLTsqhrKRN3LeO6qSN/GxohhuRv8qH9bNQ4B8W7e/vFL+0XTnmHPB4/kegunZGA4Org==",
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"license": "MIT"
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"dev": true,
"engines": {
"node": ">=10"
}
},
"node_modules/yargs": {
"version": "17.6.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz",
"integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==",
"dev": true,
"dependencies": {
"cliui": "^8.0.1",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.1.1"
},
"engines": {
"node": ">=12"
}
},
"node_modules/yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
"engines": {
"node": ">=12"
}
}
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@sparticuz/chromium",
"version": "130.0.0",
"version": "110.0.1",
"description": "Chromium Binary for Serverless Platforms",
"keywords": [
"aws",
@ -36,19 +36,18 @@
"test": "make clean && make && make pretest && make test"
},
"dependencies": {
"follow-redirects": "^1.15.9",
"tar-fs": "^3.0.6"
"follow-redirects": "^1.15.2",
"tar-fs": "^2.1.1"
},
"devDependencies": {
"@tsconfig/node20": "^20.1.4",
"@tsconfig/strictest": "^2.0.5",
"@types/follow-redirects": "^1.14.4",
"@types/node": "^20.16.10",
"@types/tar-fs": "^2.0.4",
"clean-modules": "^3.1.1",
"typescript": "^5.6.2"
"@tsconfig/node14": "^1.0.3",
"@types/follow-redirects": "^1.14.1",
"@types/node": "^18.11.18",
"@types/tar-fs": "^2.0.1",
"clean-modules": "^2.0.6",
"typescript": "^4.9.4"
},
"engines": {
"node": ">= 16"
"node": ">= 14.18.0"
}
}

View File

@ -15,62 +15,22 @@ export const isValidUrl = (input: string) => {
} catch (err) {
return false;
}
};
/**
* Determines if the running instance is inside an AWS Lambda container.
* AWS_EXECUTION_ENV is for native Lambda instances
* AWS_LAMBDA_JS_RUNTIME is for netlify instances
* @returns boolean indicating if the running instance is inside a Lambda container
*/
export const isRunningInAwsLambda = () => {
if (
process.env["AWS_EXECUTION_ENV"] &&
process.env["AWS_EXECUTION_ENV"].includes("AWS_Lambda_nodejs") &&
!process.env["AWS_EXECUTION_ENV"].includes("20.x")
) {
return true;
} else if (
process.env["AWS_LAMBDA_JS_RUNTIME"] &&
process.env["AWS_LAMBDA_JS_RUNTIME"].includes("nodejs") &&
!process.env["AWS_LAMBDA_JS_RUNTIME"].includes("20.x")
) {
return true;
}
return false;
};
export const isRunningInAwsLambdaNode20 = () => {
if (
process.env["AWS_EXECUTION_ENV"] &&
process.env["AWS_EXECUTION_ENV"].includes("20.x")
) {
return true;
} else if (
process.env["AWS_LAMBDA_JS_RUNTIME"] &&
process.env["AWS_LAMBDA_JS_RUNTIME"].includes("20.x")
) {
return true;
}
return false;
};
export const downloadAndExtract = async (url: string) =>
new Promise<string>((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) => {
const destDir = `${tmpdir()}/chromium-pack`
const extractObj = extract(destDir)
https.get(url, (response) => {
response.pipe(extractObj);
extractObj.on("finish", () => {
extractObj.on('finish', () => {
resolve(destDir);
});
})
.on("error", (err) => {
}).on('error', (err) => {
unlink(destDir, (_) => {
reject(err);
});
reject(err)
});
});
})

View File

@ -1,20 +1,9 @@
import {
access,
createWriteStream,
existsSync,
mkdirSync,
symlink,
} from "node:fs";
import { https } from "follow-redirects";
import LambdaFS from "./lambdafs";
import { join } from "node:path";
import { URL } from "node:url";
import {
downloadAndExtract,
isRunningInAwsLambda,
isValidUrl,
isRunningInAwsLambdaNode20,
} 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 {
@ -29,89 +18,56 @@ interface Viewport {
/**
* Specify device scale factor.
* See {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio | devicePixelRatio} for more info.
* @default 1
* @defaultValue 1
*/
deviceScaleFactor?: number;
/**
* Whether the `meta viewport` tag is taken into account.
* @default false
* @defaultValue false
*/
isMobile?: boolean;
/**
* Specifies if the viewport is in landscape mode.
* @default false
* @defaultValue false
*/
isLandscape?: boolean;
/**
* Specify if the viewport supports touch events.
* @default false
* @defaultValue false
*/
hasTouch?: boolean;
}
if (isRunningInAwsLambda()) {
if (process.env["FONTCONFIG_PATH"] === undefined) {
process.env["FONTCONFIG_PATH"] = "/tmp/fonts";
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';
}
if (process.env["LD_LIBRARY_PATH"] === undefined) {
process.env["LD_LIBRARY_PATH"] = "/tmp/al2/lib";
} else if (
process.env["LD_LIBRARY_PATH"].startsWith("/tmp/al2/lib") !== true
) {
process.env["LD_LIBRARY_PATH"] = [
...new Set([
"/tmp/al2/lib",
...process.env["LD_LIBRARY_PATH"].split(":"),
]),
].join(":");
}
}
if (isRunningInAwsLambdaNode20()) {
if (process.env["FONTCONFIG_PATH"] === undefined) {
process.env["FONTCONFIG_PATH"] = "/tmp/fonts";
}
if (process.env["LD_LIBRARY_PATH"] === undefined) {
process.env["LD_LIBRARY_PATH"] = "/tmp/al2023/lib";
} else if (
process.env["LD_LIBRARY_PATH"].startsWith("/tmp/al2023/lib") !== true
) {
process.env["LD_LIBRARY_PATH"] = [
...new Set([
"/tmp/al2023/lib",
...process.env["LD_LIBRARY_PATH"].split(":"),
]),
].join(":");
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(':');
}
}
class Chromium {
/**
* Determines the headless mode that chromium will run at
* https://developer.chrome.com/articles/new-headless/#try-out-the-new-headless
* @values true or "new"
*/
private static headlessMode: true | "shell" = "shell";
/**
* If true, the graphics stack and webgl is enabled,
* If false, webgl will be disabled.
* (If false, the swiftshader.tar.br file will also not extract)
*/
private static graphicsMode: boolean = true;
/**
* Downloads or symlinks a custom font and returns its basename, patching the environment so that Chromium can find it.
* If headless is not true, `null` is returned instead.
*/
static font(input: string): Promise<string> {
if (process.env["HOME"] === undefined) {
process.env["HOME"] = "/tmp";
static font(input: string): Promise<string | null> {
if (Chromium.headless !== true) {
return new Promise((resolve) => {
return resolve(null);
});
}
if (existsSync(`${process.env["HOME"]}/.fonts`) !== true) {
mkdirSync(`${process.env["HOME"]}/.fonts`);
if (process.env.HOME === undefined) {
process.env.HOME = '/tmp';
}
if (existsSync(`${process.env.HOME}/.fonts`) !== true) {
mkdirSync(`${process.env.HOME}/.fonts`);
}
return new Promise((resolve, reject) => {
@ -120,45 +76,43 @@ 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 {
https.get(input, (response) => {
let handler = url.protocol === 'http:' ? require('http').get : require('https').get;
handler(input, (response: IncomingMessage) => {
if (response.statusCode !== 200) {
return reject(`Unexpected status code: ${response.statusCode}.`);
}
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);
});
});
});
@ -171,113 +125,47 @@ class Chromium {
* The canonical list of flags can be found on https://peter.sh/experiments/chromium-command-line-switches/.
*/
static get args(): string[] {
/**
* These are the default args in puppeteer.
* https://github.com/puppeteer/puppeteer/blob/3a31070d054fa3cd8116ca31c578807ed8d6f987/packages/puppeteer-core/src/node/ChromeLauncher.ts#L185
*/
const puppeteerFlags = [
"--allow-pre-commit-input",
"--disable-background-networking",
"--disable-background-timer-throttling",
"--disable-backgrounding-occluded-windows",
"--disable-breakpad",
"--disable-client-side-phishing-detection",
"--disable-component-extensions-with-background-pages",
"--disable-component-update",
"--disable-default-apps",
"--disable-dev-shm-usage",
"--disable-extensions",
"--disable-hang-monitor",
"--disable-ipc-flooding-protection",
"--disable-popup-blocking",
"--disable-prompt-on-repost",
"--disable-renderer-backgrounding",
"--disable-sync",
"--enable-automation",
// TODO(sadym): remove '--enable-blink-features=IdleDetection' once
// IdleDetection is turned on by default.
"--enable-blink-features=IdleDetection",
"--export-tagged-pdf",
"--force-color-profile=srgb",
"--metrics-recording-only",
"--no-first-run",
"--password-store=basic",
"--use-mock-keychain",
];
const puppeteerDisableFeatures = [
"Translate",
"BackForwardCache",
// AcceptCHFrame disabled because of crbug.com/1348106.
"AcceptCHFrame",
"MediaRouter",
"OptimizationHints",
];
const puppeteerEnableFeatures = ["NetworkServiceInProcess2"];
const chromiumFlags = [
"--disable-domain-reliability", // https://github.com/GoogleChrome/chrome-launcher/blob/main/docs/chrome-flags-for-tools.md#background-networking
"--disable-print-preview", // https://source.chromium.org/search?q=lang:cpp+symbol:kDisablePrintPreview&ss=chromium
"--disable-speech-api", // https://source.chromium.org/search?q=lang:cpp+symbol:kDisableSpeechAPI&ss=chromium
"--disk-cache-size=33554432", // https://source.chromium.org/search?q=lang:cpp+symbol:kDiskCacheSize&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-pings", // https://source.chromium.org/search?q=lang:cpp+symbol:kNoPings&ss=chromium
"--single-process", // Needs to be single-process to avoid `prctl(PR_SET_NO_NEW_PRIVS) failed` error
"--font-render-hinting=none", // https://github.com/puppeteer/puppeteer/issues/2410#issuecomment-560573612
];
const chromiumDisableFeatures = [
"AudioServiceOutOfProcess",
"IsolateOrigins",
"site-per-process",
];
const chromiumEnableFeatures = ["SharedArrayBuffer"];
const graphicsFlags = [
"--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
"--window-size=1920,1080", // https://source.chromium.org/search?q=lang:cpp+symbol:kWindowSize&ss=chromium
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
];
// https://chromium.googlesource.com/chromium/src/+/main/docs/gpu/swiftshader.md
// Blocked by https://github.com/Sparticuz/chromium/issues/247
//this.graphics
// ? graphicsFlags.push("--use-gl=angle", "--use-angle=swiftshader")
// : graphicsFlags.push("--disable-webgl");
graphicsFlags.push("--use-gl=angle", "--use-angle=swiftshader");
if (Chromium.headless === true) {
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
}
const insecureFlags = [
"--allow-running-insecure-content", // https://source.chromium.org/search?q=lang:cpp+symbol:kAllowRunningInsecureContent&ss=chromium
"--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-web-security", // https://source.chromium.org/search?q=lang:cpp+symbol:kDisableWebSecurity&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
];
const headlessFlags = [
this.headless === "shell" ? "--headless='shell'" : "--headless",
];
return [
...puppeteerFlags,
...chromiumFlags,
`--disable-features=${[
...puppeteerDisableFeatures,
...chromiumDisableFeatures,
].join(",")}`,
`--enable-features=${[
...puppeteerEnableFeatures,
...chromiumEnableFeatures,
].join(",")}`,
...graphicsFlags,
...insecureFlags,
...headlessFlags,
];
return result;
}
/**
* Returns sensible default viewport settings for serverless environments.
* Returns sensible default viewport settings.
*/
static get defaultViewport(): Required<Viewport> {
return {
@ -299,25 +187,19 @@ 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 is a valid URL, download and extract the file. It will extract to /tmp/chromium-pack
* and executablePath will be recursively called on that location, which will then extract
* the brotli files to the correct locations
*/
if (input && isValidUrl(input)) {
return this.executablePath(await downloadAndExtract(input));
}
/**
* If input is defined, use that as the location of the brotli files,
* 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.
@ -326,85 +208,39 @@ class Chromium {
throw new Error(`The input directory "${input}" does not exist.`);
}
// Extract the required files
const promises = [
LambdaFS.inflate(`${input}/chromium.br`),
LambdaFS.inflate(`${input}/fonts.tar.br`),
LambdaFS.inflate(`${input}/swiftshader.tar.br`),
];
if (this.graphics) {
// Only inflate graphics stack if needed
promises.push(LambdaFS.inflate(`${input}/swiftshader.tar.br`));
}
if (isRunningInAwsLambda()) {
// If running in AWS Lambda, extract more required files
promises.push(LambdaFS.inflate(`${input}/al2.tar.br`));
}
if (isRunningInAwsLambdaNode20()) {
promises.push(LambdaFS.inflate(`${input}/al2023.tar.br`));
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`));
}
// Await all extractions
const result = await Promise.all(promises);
// Returns the first result of the promise, which is the location of the `chromium` binary
return result.shift() as string;
}
/**
* Returns the headless mode.
* "shell" means the 'old' (legacy, chromium < 112) headless mode.
* `true` means the 'new' headless mode.
* https://developer.chrome.com/articles/new-headless/#try-out-the-new-headless
* @returns true | "shell"
* Returns a boolean indicating if we are running on AWS Lambda or Google Cloud Functions.
* True is returned if the NODE_ENV is set to 'test' for easier integration testing.
* False is returned if Serverless environment variables `IS_LOCAL` or `IS_OFFLINE` are set.
*/
public static get headless() {
return this.headlessMode;
static get headless() {
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',
];
/**
* Sets the headless mode.
* "shell" means the 'old' (legacy, chromium < 112) headless mode.
* `true` means the 'new' headless mode.
* https://developer.chrome.com/articles/new-headless/#try-out-the-new-headless
* @default "shell"
*/
public static set setHeadlessMode(value: true | "shell") {
if (
(typeof value === "string" && value !== "shell") ||
(typeof value === "boolean" && value !== true)
) {
throw new Error(
`Headless mode must be either \`true\` or 'shell', you entered '${value}'`
);
}
this.headlessMode = value;
}
/**
* Returns whether the graphics stack is enabled or disabled
* @returns boolean
*/
public static get graphics() {
return this.graphicsMode;
}
/**
* Sets whether the graphics stack is enabled or disabled.
* @param true means the stack is enabled. WebGL will work.
* @param false means that the stack is disabled. WebGL will not work.
* `false` will also skip the extract of the graphics driver, saving about a second during initial extract
* @default true
*/
public static set setGraphicsMode(value: boolean) {
if (typeof value !== "boolean") {
throw new Error(
`Graphics mode must be a boolean, you entered '${value}'`
);
}
// Disabling 'disabling the gpu'
// Blocked by https://github.com/Sparticuz/chromium/issues/247
// this.graphicsMode = value;
this.graphicsMode = true;
return environments.some((key) => process.env[key] !== undefined);
}
}

View File

@ -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,15 +11,7 @@ class LambdaFS {
* @param filePath Path of the file to decompress.
*/
static inflate(filePath: string): Promise<string> {
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")) {
@ -38,33 +30,27 @@ 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);
}
@ -72,4 +58,4 @@ class LambdaFS {
}
}
export default LambdaFS;
export = LambdaFS;

View File

@ -1,11 +1,9 @@
{
"extends": ["@tsconfig/node20/tsconfig", "@tsconfig/strictest"],
"extends": "@tsconfig/node14/tsconfig.json",
"compilerOptions": {
"declaration": true,
"lib": ["dom", "ES2023"],
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "build"
"lib": ["dom"],
"outDir": "build",
},
"include": ["source"]
}