Repository: node-formidable/formidable
Branch: master
Commit: 44768be86149
Files: 120
Total size: 472.6 KB
Directory structure:
gitextract_b0r5txre/
├── .all-contributorsrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.cjs
├── .github/
│ ├── .kodiak.toml
│ ├── dependabot.yml
│ └── workflows/
│ ├── codeql-analysis.yml
│ └── main.yml
├── .gitignore
├── .prettierignore
├── .prettierrc.js
├── CHANGELOG.md
├── LICENSE
├── README.md
├── README_pt_BR.md
├── VERSION_NOTES.md
├── benchmark/
│ ├── 2022-11-30-i5-9600k.txt
│ ├── e2e.txt
│ ├── index.js
│ └── server.js
├── examples/
│ ├── express-middleware.js
│ ├── forceBuffer.js
│ ├── json.js
│ ├── log-file-content-to-console.js
│ ├── multipart-parser.js
│ ├── multiples.js
│ ├── store-files-on-s3.js
│ ├── upload-multiple-files.js
│ ├── urlencoded-no-enctype.js
│ ├── with-express.js
│ ├── with-http.js
│ └── with-koa2.js
├── nyc.config.js
├── package.json
├── pnpm-lock.yaml
├── src/
│ ├── Formidable.js
│ ├── FormidableError.js
│ ├── PersistentFile.js
│ ├── VolatileFile.js
│ ├── helpers/
│ │ ├── firstValues.js
│ │ └── readBooleans.js
│ ├── index.js
│ ├── parsers/
│ │ ├── Dummy.js
│ │ ├── JSON.js
│ │ ├── Multipart.js
│ │ ├── OctetStream.js
│ │ ├── Querystring.js
│ │ ├── StreamingQuerystring.js
│ │ └── index.js
│ └── plugins/
│ ├── index.js
│ ├── json.js
│ ├── multipart.js
│ ├── octetstream.js
│ └── querystring.js
├── test/
│ ├── fixture/
│ │ ├── file/
│ │ │ ├── funkyfilename.txt
│ │ │ ├── plain.txt
│ │ │ └── second-plaintext.txt
│ │ ├── http/
│ │ │ ├── encoding/
│ │ │ │ ├── beta-sticker-1.png.http
│ │ │ │ ├── binaryfile.tar.gz.http
│ │ │ │ ├── blank.gif.http
│ │ │ │ ├── menu_separator.png.http
│ │ │ │ └── plain.txt.http
│ │ │ ├── misc/
│ │ │ │ ├── boundary-substring-json.http
│ │ │ │ ├── empty-multipart.http
│ │ │ │ ├── empty-multipart2.http
│ │ │ │ ├── empty-urlencoded.http
│ │ │ │ ├── empty.http
│ │ │ │ └── minimal.http
│ │ │ ├── no-filename/
│ │ │ │ ├── filename-name.http
│ │ │ │ └── generic.http
│ │ │ ├── preamble/
│ │ │ │ ├── crlf.http
│ │ │ │ └── preamble.http
│ │ │ ├── special-chars-in-filename/
│ │ │ │ ├── info.md
│ │ │ │ ├── line-separator.http
│ │ │ │ ├── osx-chrome-13.http
│ │ │ │ ├── osx-firefox-3.6.http
│ │ │ │ ├── osx-safari-5.http
│ │ │ │ ├── xp-chrome-12.http
│ │ │ │ ├── xp-ie-7.http
│ │ │ │ ├── xp-ie-8.http
│ │ │ │ └── xp-safari-5.http
│ │ │ └── workarounds/
│ │ │ ├── missing-hyphens1.http
│ │ │ └── missing-hyphens2.http
│ │ ├── js/
│ │ │ ├── encoding.js
│ │ │ ├── misc.js
│ │ │ ├── no-filename.js
│ │ │ ├── preamble.js
│ │ │ ├── special-chars-in-filename.js
│ │ │ └── workarounds.js
│ │ ├── multi_video.upload
│ │ └── multipart.js
│ ├── integration/
│ │ ├── file-write-stream-handler-option.test.js
│ │ ├── fixtures.test.js
│ │ ├── json.test.js
│ │ ├── octet-stream.test.js
│ │ └── store-files-option.test.js
│ ├── standalone/
│ │ ├── connection-aborted.test.js
│ │ ├── content-transfer-encoding.test.js
│ │ ├── issue-46.test.js
│ │ └── keep-alive-error.test.js
│ ├── tools/
│ │ └── base64.html
│ └── unit/
│ ├── custom-plugins.test.js
│ ├── formidable.test.js
│ ├── multipart-parser.test.js
│ ├── persistent-file.disabled-test.js
│ ├── querystring-parser.test.js
│ └── volatile-file.test.js
├── test-legacy/
│ ├── .DS_Store
│ ├── README.md
│ ├── common.js
│ ├── integration/
│ │ └── test-multipart-parser.js
│ ├── simple/
│ │ ├── test-file.js
│ │ └── test-incoming-form.js
│ └── system/
│ └── test-multi-video-upload.js
├── test-node/
│ └── standalone/
│ ├── createDirsFromUploads.test.js
│ ├── end-event-emitted-twice.test.js
│ ├── multipart_parser.test.js
│ └── promise.test.js
└── tool/
├── record.js
└── rollup.config.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .all-contributorsrc
================================================
{
"projectName": "node-formidable",
"projectOwner": "node-formidable",
"repoType": "github",
"repoHost": "https://github.com",
"imageSize": 100,
"contributorsPerLine": 6,
"commitConvention": "angular",
"commit": true,
"skipCi": true,
"files": [
"README.md"
],
"contributors": [
{
"login": "felixge",
"name": "Felix Geisendörfer",
"avatar_url": "https://avatars3.githubusercontent.com/u/15000?s=460&v=4",
"profile": "https://twitter.com/felixge",
"contributions": [
"code",
"design",
"ideas",
"doc"
]
},
{
"login": "tunnckoCore",
"name": "Charlike Mike Reagent",
"avatar_url": "https://avatars3.githubusercontent.com/u/5038030?v=4",
"profile": "https://tunnckoCore.com",
"contributions": [
"bug",
"infra",
"design",
"code",
"doc",
"example",
"ideas",
"maintenance",
"test"
]
},
{
"login": "kedarv",
"name": "Kedar",
"avatar_url": "https://avatars1.githubusercontent.com/u/1365665?v=4",
"profile": "https://github.com/kedarv",
"contributions": [
"code",
"test",
"question",
"bug"
]
},
{
"login": "GrosSacASac",
"name": "Walle Cyril",
"avatar_url": "https://avatars0.githubusercontent.com/u/5721194?v=4",
"profile": "https://github.com/GrosSacASac",
"contributions": [
"question",
"bug",
"code",
"financial",
"ideas",
"maintenance"
]
},
{
"login": "xarguments",
"name": "Xargs",
"avatar_url": "https://avatars2.githubusercontent.com/u/40522463?v=4",
"profile": "https://github.com/xarguments",
"contributions": [
"question",
"bug",
"code",
"maintenance"
]
},
{
"login": "Amit-A",
"name": "Amit-A",
"avatar_url": "https://avatars1.githubusercontent.com/u/7987238?v=4",
"profile": "https://github.com/Amit-A",
"contributions": [
"question",
"bug",
"code"
]
},
{
"login": "charmander",
"name": "Charmander",
"avatar_url": "https://avatars1.githubusercontent.com/u/1889843?v=4",
"profile": "https://charmander.me/",
"contributions": [
"question",
"bug",
"code",
"ideas",
"maintenance"
]
},
{
"login": "DylanPiercey",
"name": "Dylan Piercey",
"avatar_url": "https://avatars2.githubusercontent.com/u/4985201?v=4",
"profile": "https://twitter.com/dylan_piercey",
"contributions": [
"ideas"
]
},
{
"login": "ad-m",
"name": "Adam Dobrawy",
"avatar_url": "https://avatars1.githubusercontent.com/u/3618479?v=4",
"profile": "http://ochrona.jawne.info.pl",
"contributions": [
"bug",
"doc"
]
},
{
"login": "amitrohatgi",
"name": "amitrohatgi",
"avatar_url": "https://avatars3.githubusercontent.com/u/12177021?v=4",
"profile": "https://github.com/amitrohatgi",
"contributions": [
"ideas"
]
},
{
"login": "fengxinming",
"name": "Jesse Feng",
"avatar_url": "https://avatars2.githubusercontent.com/u/6262382?v=4",
"profile": "https://github.com/fengxinming",
"contributions": [
"bug"
]
},
{
"login": "quantumsheep",
"name": "Nathanael Demacon",
"avatar_url": "https://avatars1.githubusercontent.com/u/7271496?v=4",
"profile": "https://qtmsheep.com",
"contributions": [
"question",
"code",
"review"
]
},
{
"login": "MunMunMiao",
"name": "MunMunMiao",
"avatar_url": "https://avatars1.githubusercontent.com/u/18216142?v=4",
"profile": "https://github.com/MunMunMiao",
"contributions": [
"bug"
]
},
{
"login": "gabipetrovay",
"name": "Gabriel Petrovay",
"avatar_url": "https://avatars0.githubusercontent.com/u/1170398?v=4",
"profile": "https://github.com/gabipetrovay",
"contributions": [
"bug",
"code"
]
},
{
"login": "Elzair",
"name": "Philip Woods",
"avatar_url": "https://avatars0.githubusercontent.com/u/2352818?v=4",
"profile": "https://github.com/Elzair",
"contributions": [
"code",
"ideas"
]
},
{
"login": "dmolim",
"name": "Dmitry Ivonin",
"avatar_url": "https://avatars2.githubusercontent.com/u/7090374?v=4",
"profile": "https://github.com/dmolim",
"contributions": [
"doc"
]
},
{
"login": "masterkain",
"name": "Claudio Poli",
"avatar_url": "https://avatars1.githubusercontent.com/u/12844?v=4",
"profile": "https://audiobox.fm",
"contributions": [
"code"
]
}
]
}
================================================
FILE: .editorconfig
================================================
# http://editorconfig.org/
root = true
[*]
indent_style = space
indent_size = 2
tab_width = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = true
================================================
FILE: .eslintignore
================================================
# Ignore everything!
*
# de-ignores: add here what you want to be committed
!*.*js*
!*.ts*
!*.md*
!**/src
!**/src/**
!**/test
!**/test/**
!**/*tests*
!**/*tests*/**
!**/benchmark*
!**/benchmark*/**
!**/example*
!**/example*/**
# re-ignores: add here what you want to be ignored again
test/tmp
================================================
FILE: .eslintrc.cjs
================================================
'use strict';
const airbnbBase = require('eslint-config-airbnb-base');
// eslint-disable-next-line import/no-dynamic-require
const bestPractices = require(airbnbBase.extends[0]);
const ignoredProps = bestPractices.rules[
'no-param-reassign'
][1].ignorePropertyModificationsFor.concat(
'err',
'x',
'_',
'opts',
'options',
'settings',
'config',
'cfg',
);
// Additional rules that are specific and overriding previous
const additionalChanges = {
strict: 'off',
// Enforce using named functions when regular function is used,
// otherwise use arrow functions
'func-names': ['error', 'always'],
// Always use parens (for consistency).
// https://eslint.org/docs/rules/arrow-parens
'arrow-parens': ['error', 'always', { requireForBlockBody: true }],
'prefer-arrow-callback': [
'error',
{ allowNamedFunctions: true, allowUnboundThis: true },
],
// http://eslint.org/docs/rules/max-params
'max-params': ['error', { max: 3 }],
// http://eslint.org/docs/rules/max-statements
'max-statements': ['error', { max: 20 }],
// http://eslint.org/docs/rules/max-statements-per-line
'max-statements-per-line': ['error', { max: 1 }],
// http://eslint.org/docs/rules/max-nested-callbacks
'max-nested-callbacks': ['error', { max: 4 }],
// http://eslint.org/docs/rules/max-depth
'max-depth': ['error', { max: 4 }],
// enforces no braces where they can be omitted
// https://eslint.org/docs/rules/arrow-body-style
// Never enable for object literal.
'arrow-body-style': [
'error',
'as-needed',
{ requireReturnForObjectLiteral: false },
],
// Allow functions to be use before define because:
// 1) they are hoisted,
// 2) because ensure read flow is from top to bottom
// 3) logically order of the code.
// 4) the only addition is 'typedefs' option, see overrides for TS files
'no-use-before-define': [
'error',
{
functions: false,
classes: true,
variables: true,
},
],
// Same as AirBnB, but adds `opts`, `options`, `x` and `err` to exclusions!
// disallow reassignment of function parameters
// disallow parameter object manipulation except for specific exclusions
// rule: https://eslint.org/docs/rules/no-param-reassign.html
'no-param-reassign': [
'error',
{
props: true,
ignorePropertyModificationsFor: ignoredProps,
},
],
// disallow declaration of variables that are not used in the code
'no-unused-vars': [
'error',
{
ignoreRestSiblings: true, // airbnb's default
vars: 'all', // airbnb's default
varsIgnorePattern: '^(?:$$|xx|_|__|[iI]gnor(?:e|ing|ed))',
args: 'after-used', // airbnb's default
argsIgnorePattern: '^(?:$$|xx|_|__|[iI]gnor(?:e|ing|ed))',
// catch blocks are handled by Unicorns
caughtErrors: 'none',
// caughtErrorsIgnorePattern: '^(?:$$|xx|_|__|[iI]gnor(?:e|ing|ed))',
},
],
};
const importRules = {
'import/namespace': ['error', { allowComputed: true }],
'import/no-absolute-path': 'error',
'import/no-webpack-loader-syntax': 'error',
'import/no-self-import': 'error',
// Enable this sometime in the future when Node.js has ES2015 module support
// 'import/no-cycle': 'error',
// Disabled as it doesn't work with TypeScript
// 'import/newline-after-import': 'error',
'import/no-amd': 'error',
'import/no-duplicates': 'error',
// Enable this sometime in the future when Node.js has ES2015 module support
// 'import/unambiguous': 'error',
// Enable this sometime in the future when Node.js has ES2015 module support
// 'import/no-commonjs': 'error',
// Looks useful, but too unstable at the moment
// 'import/no-deprecated': 'error',
'import/no-extraneous-dependencies': 'off',
'import/no-mutable-exports': 'error',
'import/no-named-as-default-member': 'error',
'import/no-named-as-default': 'error',
// Disabled because it's buggy and it also doesn't work with TypeScript
// 'import/no-unresolved': [
// 'error',
// {
// commonjs: true
// }
// ],
'import/order': 'error',
'import/no-unassigned-import': [
'error',
{ allow: ['@babel/polyfill', '@babel/register'] },
],
'import/prefer-default-export': 'off',
// Ensure more web-compat
// ! note that it doesn't work in CommonJS
// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/extensions.md
'import/extensions': 'off',
// ? Always use named exports. Enable?
// 'import/no-default-export': 'error',
// ? enable?
'import/exports-last': 'off',
// todo: Enable in future.
// Ensures everything is tested (all exports should be used).
// For cases when you don't want or can't test, add eslint-ignore comment!
// see: https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-unused-modules.md
'import/no-unused-modules': 'off',
'import/no-useless-path-segments': ['error', { noUselessIndex: false }],
};
module.exports = {
env: {
es6: true,
es2020: true,
jest: true,
node: true,
commonjs: true,
},
extends: ['eslint:recommended', 'airbnb-base', 'plugin:prettier/recommended'],
plugins: ['prettier'],
rules: {
...additionalChanges,
...importRules,
},
};
================================================
FILE: .github/.kodiak.toml
================================================
# .kodiak.toml
# Minimal config. version is the only required field.
version = 1
[merge]
automerge_label = "ship it"
require_automerge_label = true
block_on_neutral_required_check_runs = true
blocking_labels = ["wip", "do not merge"]
delete_branch_on_merge = true
notify_on_conflict = true
optimistic_updates = false
prioritize_ready_to_merge = true
[merge.message]
title = "pull_request_title"
body = "pull_request_body"
body_type = "markdown"
include_pr_number = true
include_coauthors = true
include_pull_request_url = true
cut_body_after = ""
cut_body_before = ""
[merge.automerge_dependencies]
# only auto merge "minor" and "patch" version upgrades.
# do not automerge "major" version upgrades.
versions = ["minor", "patch"]
usernames = ["dependabot", "renovate"]
[approve]
auto_approve_usernames = ["dependabot", "renovate"]
[update]
always = true
require_automerge_label = true
================================================
FILE: .github/dependabot.yml
================================================
# Versions and updates, dependabot.yml
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
# Maintain dependencies for npm
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
time: "02:00"
# No one cares about dependency labels, they should be auto-merged
labels: []
allow:
# Allow both direct and indirect updates for all packages
- dependency-type: "all"
rebase-strategy: "auto"
# reviewers: []
================================================
FILE: .github/workflows/codeql-analysis.yml
================================================
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '0 2 * * *'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
steps:
- name: Checkout repository
uses: actions/checkout@v5
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
================================================
FILE: .github/workflows/main.yml
================================================
name: ci
# on:
# push:
# branches: [ master ]
# pull_request:
# branches: [ master ]
# jobs:
# # lint:
# # name: Lint
# # runs-on: ubuntu-latest
# # steps:
# # - uses: actions/checkout@v5
# # - name: Set up Node.js
# # uses: actions/setup-node@v5
# # with:
# # node-version: '20'
# # cache: 'npm'
# # - name: Install dependencies
# # run: npm install
# # - name: Run lint
# # run: npm run lint
# test:
# name: Test on ${{ matrix.os }}
# runs-on: ${{ matrix.os }}
# strategy:
# fail-fast: false
# matrix:
# os: [ubuntu-latest, macos-latest]
# node-version: ['lts/*']
# steps:
# - uses: actions/checkout@v5
# - name: Set up Node.js ${{ matrix.node-version }}
# uses: actions/setup-node@v5
# with:
# node-version: ${{ matrix.node-version }}
# cache: 'npm'
# - name: Install dependencies
# run: npm install
# - name: Run tests
# run: npm test
on:
push:
branches:
- '*'
pull_request:
branches:
- '*'
jobs:
build-and-test:
name: Test on ${{ matrix.os }} with Node.js ${{matrix.node-version}}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
node-version: [18, 20, 22, 'lts/*']
steps:
- name: Checkout
uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
run_install: false
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v5
with:
node-version: ${{ matrix.node-version }}
cache: pnpm
- name: Install dependencies
run: pnpm install
- name: Run tests
run: pnpm test
================================================
FILE: .gitignore
================================================
# Ignore everything!
*
*~*
# de-ignores: add here what you want to be committed
!logo.png
!logo.jpg
!test-legacy
!tool
!*.*js*
!*.ts*
!*.md*
!.*rc
!.*ignore
!LICENSE
!.editorconfig
!package.json
!yarn.lock
!pnpm-lock.yaml
!**/src
!**/src/**
!**/test
!**/test/**
!**/test-node
!**/test-node/**
!**/*tests*
!**/*tests*/**
!**/.github
!**/.github/**
!**/example*
!**/example*/**
!**/benchmark*
!**/benchmark*/**
# re-ignores: add here what you want to be ignored again
test/tmp
# !src/*.js
# !src/*.ts
# !test
# !test/*.js
# !test/*.ts
# !test/**/*.js
# !test/**/*.ts
# !*/__tests__
# *.tsbuildinfo
# .*cache
# *.cache
# test/tmp
# *.upload
# *.un~
# # Build environment
# dist
# # Package managers lockfiles
# package-lock.json
# shrinkwrap.json
# # Logs
# logs
# *.log
# *~
# # Runtime data
# pids
# *.pid
# *.seed
# *.pid.lock
# # Directory for instrumented libs generated by jscoverage/JSCover
# lib-cov
# # Coverage directory used by tools like istanbul
# /coverage
# # nyc test coverage
# .nyc_output
# # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
# .grunt
# # Bower dependency directory (https://bower.io/)
# bower_components
# # node-waf configuration
# .lock-wscript
# # Compiled binary addons (https://nodejs.org/api/addons.html)
# build/Release
# # Dependency directories
# node_modules/
# jspm_packages/
# # TypeScript v1 declaration files
# typings/
# # Optional npm cache directory
# .npm
# # Optional eslint cache
# .eslintcache
# # Optional REPL history
# .node_repl_history
# # Output of 'npm pack'
# *.tgz
# # Yarn Integrity file
# .yarn-integrity
# # dotenv environment variables file
# .env
# # next.js build output
# .next
benchmark/testuploads/
================================================
FILE: .prettierignore
================================================
# Ignore everything!
*
# de-ignores: add here what you want to be committed
!*.*js*
!*.ts*
!*.md*
!*.y*ml
!**/src
!**/src/**
!**/test
!**/test/**
!**/*tests*
!**/*tests*/**
!**/.github
!**/.github/**
!**/benchmark*
!**/benchmark*/**/*.js
!**/example*
!**/example*/**
# re-ignores: add here what you want to be ignored again
test/tmp
test/fixture/file
test/fixture/http
*.upload
CHANGELOG.md
# CHANGELOG.md
# LICENSE*
# dist
# test/tmp
# test/fixture/http
# test/fixture/file
# test/fixture/multi*
# test/tools
# # fixtures
# # __fixture__
# # __fixtures__
# *.map
# *.lock
# *.js.snap
# coverage
# *.ico
# *.png
# *.svg
# *.jpeg
# *.jpg
# !.all-contributorsrc
# !.*rc.js
# !.verb*.md
# patches
# **/static/**/*.css
# *.tsbuildinfo
# .*cache
# *.cache
# # Package managers lockfiles
# package-lock.json
# shrinkwrap.json
# pnpm-lock.json
# # Logs
# logs
# *.log
# *~
# # Runtime data
# pids
# *.pid
# *.seed
# *.pid.lock
# # Directory for instrumented libs generated by jscoverage/JSCover
# lib-cov
# # Coverage directory used by tools like istanbul
# coverage
# # nyc test coverage
# .nyc_output
# # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
# .grunt
# # Bower dependency directory (https://bower.io/)
# bower_components
# # node-waf configuration
# .lock-wscript
# # Compiled binary addons (https://nodejs.org/api/addons.html)
# build/Release
# # Dependency directories
# node_modules/
# jspm_packages/
# # TypeScript v1 declaration files
# typings/
# # Optional npm cache directory
# .npm
# # Optional eslint cache
# .eslintcache
# # Optional REPL history
# .node_repl_history
# # Output of 'npm pack'
# *.tgz
# # Yarn Integrity file
# .yarn-integrity
# # dotenv environment variables file
# .env
# # next.js build output
# .next
================================================
FILE: .prettierrc.js
================================================
'use strict';
const config = require('@tunnckocore/prettier-config');
module.exports = {
...config,
overrides: [
{
files: ['**/*.md*'],
options: {
proseWrap: 'always',
printWidth: 80,
},
},
{
files: ['**/.all-contributorsrc'],
options: {
parser: 'json-stringify',
singleQuote: false,
},
},
],
};
================================================
FILE: CHANGELOG.md
================================================
# Changelog
### 3.5.4
- fix the `os.machine` breaking some dependents, fix [#994](https://github.com/node-formidable/formidable/issues/994)
- add Node 16, 18, 20, 22 to CI/CD
### 3.5.3
- security report by ZAST.AI help for some vulnerabilities addressing (primarily the random names generation)
- update failing tests
- update CI/CD workflows and actions;
- update CodeQL github action for security analysis
- update readme, links and badges
- update to use cuid2 (battle-tested `@paralleldrive/cuid2` package) for better random names - should not be breaking anything since it's still 25 characters long, but a lot safer and faster.
### 3.5.2
* fix: ([#982](https://github.com/node-formidable/formidable/pull/982)) make it easier to import hexoid with webpack
### 3.5.1
* fix: ([#945](https://github.com/node-formidable/formidable/pull/945)) multipart parser fix: flush or fail always (don't hang)
### 3.5.0
* feature: ([#944](https://github.com/node-formidable/formidable/pull/944)) Dual package: Can be imported as ES module and required as commonjs module
### 3.4.0
* feature: ([#940](https://github.com/node-formidable/formidable/pull/940)) form.parse returns a promise if no callback is provided
* it resolves with an array `[fields, files]`
### 3.3.2
* feature: ([#855](https://github.com/node-formidable/formidable/pull/855)) add options.createDirsFromUploads, see README for usage
* form.parse is an async function (ignore the promise)
* benchmarks: add e2e becnhmark with as many request as possible per second
* npm run to display all the commands
* mark as latest on npm
### 3.2.5
* fix: ([#881](https://github.com/node-formidable/formidable/pull/881)) fail earlier when maxFiles is exceeded
### 3.2.4
* fix: ([#857](https://github.com/node-formidable/formidable/pull/857)) improve keep extension
* The code from before 3.2.4 already removed some characters from the file extension. But not always. So it was inconsistent.
* The new code cuts the file extension at the first invalid character (invalid in a file extension).
* The characters that are considered invalid inside a file extension are all except the . numbers and a-Z.
* This change only has an effect if filename option is not used and keepextension option is used
### 3.2.3
* fix: ([#852](https://github.com/node-formidable/formidable/pull/852)) end event is emitted once
### 3.2.2
* refactor: ([#801](https://github.com/node-formidable/formidable/pull/801))
### 3.2.1
* fix: do not let empty file on error ([#796](https://github.com/node-formidable/formidable/pull/796))
* it was probably due to the fact that .destroy on a file stream does not always complete on time
### 3.2.0
* feat: maxFileSize option is now per file (as the name suggests) ([#791](https://github.com/node-formidable/formidable/pull/791))
* feat: add maxFiles option, default Infinity
* feat: add maxTotalFileSize, default is maxFileSize (for backwards compatibility)
* fix: minFileSize is per file
* fix: allowEmptyFiles fix in cases where one file is not empty
* fix: allowEmptyFiles false option by default
* fix: rename wrongly named error
* refactor: rename wrongly named maxFileSize into maxTotalFileSize
### 3.1.5
* fix: PersistentFile.toString ([#796](https://github.com/node-formidable/formidable/pull/796))
### 3.1.4
* fix: add missing pluginFailed error ([#794](https://github.com/node-formidable/formidable/pull/794))
* refactor: use explicit node imports (#786)
### 3.1.1
* feat: handle top level json array, string and number
### 3.1.0
* feat: add firstValues, readBooleans helpers
### 3.0.0
* feat: remove options.multiples ([#730](https://github.com/node-formidable/formidable/pull/730))
* use modern URLSearchParams https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams internally
* files and fields values are always arrays
* fields with [] in the name do not receive special treatment
* remove unused qs and querystring dependency
* feat: Use ES modules ([#727](https://github.com/node-formidable/formidable/pull/727))
* options.enabledPlugins must contain the plugin themselves instead of the plugins names
### 2.0.0
* feat: files are detected if a mimetype is present (previously it was based on filename)
* feat: add options.filter ([#716](https://github.com/node-formidable/formidable/pull/716))
* feat: add code and httpCode to most errors ([#686](https://github.com/node-formidable/formidable/pull/686))
* rename: option.hash into option.hashAlgorithm ([#689](https://github.com/node-formidable/formidable/pull/689))
* rename: file.path into file.filepath ([#689](https://github.com/node-formidable/formidable/pull/689))
* rename: file.type into file.mimetype ([#689](https://github.com/node-formidable/formidable/pull/689))
* refactor: split file.name into file.newFilename and file.originalFilename ([#689](https://github.com/node-formidable/formidable/pull/689))
* feat: prevent directory traversal attacks by default ([#689](https://github.com/node-formidable/formidable/pull/689))
* meta: stop including test files in npm ([7003c](https://github.com/node-formidable/formidable/commit/7003cd6133f90c384081accb51743688d5e1f4be))
* fix: handle invalid filenames ([d0a34](https://github.com/node-formidable/formidable/commit/d0a3484b048b8c177e62d66aecb03f5928f7a857))
* feat: add fileWriteStreamHandler option
* feat: add allowEmptyFiles and minFileSize options
* feat: Array support for fields and files ([#380](https://github.com/node-formidable/node-formidable/pull/380), [#340](https://github.com/node-formidable/node-formidable/pull/340), [#367](https://github.com/node-formidable/node-formidable/pull/367), [#33](https://github.com/node-formidable/node-formidable/issues/33), [#498](https://github.com/node-formidable/node-formidable/issues/498), [#280](https://github.com/node-formidable/node-formidable/issues/280), [#483](https://github.com/node-formidable/node-formidable/issues/483))
* possible partial fix of [#386](https://github.com/node-formidable/node-formidable/pull/386) with #380 (need tests and better implementation)
* refactor: use hasOwnProperty in check against files/fields ([#522](https://github.com/node-formidable/node-formidable/pull/522))
* meta: do not promote `IncomingForm` and add `exports.default` ([#529](https://github.com/node-formidable/node-formidable/pull/529))
* meta: Improve examples and tests ([#523](https://github.com/node-formidable/node-formidable/pull/523))
* refactor: First step of Code quality improvements ([#525](https://github.com/node-formidable/node-formidable/pull/525))
* chore(funding): remove patreon & add npm funding field ([#525](https://github.com/node-formidable/node-formidable/pull/532)
* feat: use Modern Streams API ([#531](https://github.com/node-formidable/node-formidable/pull/531))
* fix: urlencoded parsing to emit end [#543](https://github.com/node-formidable/node-formidable/pull/543), introduced in [#531](https://github.com/node-formidable/node-formidable/pull/531)
* fix(tests): include multipart and qs parser unit tests, part of [#415](https://github.com/node-formidable/node-formidable/issues/415)
* fix: reorganize exports + move parsers to `src/parsers/`
* fix: update docs and examples [#544](https://github.com/node-formidable/node-formidable/pull/544) ([#248](https://github.com/node-formidable/node-formidable/issues/248), [#335](https://github.com/node-formidable/node-formidable/issues/335), [#371](https://github.com/node-formidable/node-formidable/issues/371), [#372](https://github.com/node-formidable/node-formidable/issues/372), [#387](https://github.com/node-formidable/node-formidable/issues/387), partly [#471](https://github.com/node-formidable/node-formidable/issues/471), [#535](https://github.com/node-formidable/node-formidable/issues/535))
* feat: introduce Plugins API, fix silent failing tests ([#545](https://github.com/node-formidable/node-formidable/pull/545), [#391](https://github.com/node-formidable/node-formidable/pull/391), [#407](https://github.com/node-formidable/node-formidable/pull/407), [#386](https://github.com/node-formidable/node-formidable/pull/386), [#374](https://github.com/node-formidable/node-formidable/pull/374), [#521](https://github.com/node-formidable/node-formidable/pull/521), [#267](https://github.com/node-formidable/node-formidable/pull/267))
* fix: exposing file writable stream errors ([#520](https://github.com/node-formidable/node-formidable/pull/520), [#316](https://github.com/node-formidable/node-formidable/pull/316), [#469](https://github.com/node-formidable/node-formidable/pull/469), [#470](https://github.com/node-formidable/node-formidable/pull/470))
* feat: custom file (re)naming, thru options.filename ([#591](https://github.com/node-formidable/node-formidable/pull/591), [#84](https://github.com/node-formidable/node-formidable/issues/84), [#86](https://github.com/node-formidable/node-formidable/issues/86), [#94](https://github.com/node-formidable/node-formidable/issues/94), [#154](https://github.com/node-formidable/node-formidable/issues/154), [#158](https://github.com/node-formidable/node-formidable/issues/158), [#488](https://github.com/node-formidable/node-formidable/issues/488), [#595](https://github.com/node-formidable/node-formidable/issues/595))
### v1.2.1 (2018-03-20)
* `maxFileSize` option with default of 200MB (Charlike Mike Reagent, Nima Shahri)
* Simplified buffering in JSON parser to avoid denial of service attack (Kornel)
* Fixed upload file cleanup on aborted requests (liaoweiqiang)
* Fixed error handling of closed _writeStream (Vitalii)
### v1.1.1 (2017-01-15)
* Fix DeprecationWarning about os.tmpDir() (Christian)
* Update `buffer.write` order of arguments for Node 7 (Kornel Lesiński)
* JSON Parser emits error events to the IncomingForm (alessio.montagnani)
* Improved Content-Disposition parsing (Sebastien)
* Access WriteStream of fs during runtime instead of include time (Jonas Amundsen)
* Use built-in toString to convert buffer to hex (Charmander)
* Add hash to json if present (Nick Stamas)
* Add license to package.json (Simen Bekkhus)
### v1.0.14 (2013-05-03)
* Add failing hash tests. (Ben Trask)
* Enable hash calculation again (Eugene Girshov)
* Test for immediate data events (Tim Smart)
* Re-arrange IncomingForm#parse (Tim Smart)
### v1.0.13
* Only update hash if update method exists (Sven Lito)
* According to travis v0.10 needs to go quoted (Sven Lito)
* Bumping build node versions (Sven Lito)
* Additional fix for empty requests (Eugene Girshov)
* Change the default to 1000, to match the new Node behaviour. (OrangeDog)
* Add ability to control maxKeys in the querystring parser. (OrangeDog)
* Adjust test case to work with node 0.9.x (Eugene Girshov)
* Update package.json (Sven Lito)
* Path adjustment according to eb4468b (Markus Ast)
### v1.0.12
* Emit error on aborted connections (Eugene Girshov)
* Add support for empty requests (Eugene Girshov)
* Fix name/filename handling in Content-Disposition (jesperp)
* Tolerate malformed closing boundary in multipart (Eugene Girshov)
* Ignore preamble in multipart messages (Eugene Girshov)
* Add support for application/json (Mike Frey, Carlos Rodriguez)
* Add support for Base64 encoding (Elmer Bulthuis)
* Add File#toJSON (TJ Holowaychuk)
* Remove support for Node.js 0.4 & 0.6 (Andrew Kelley)
* Documentation improvements (Sven Lito, Andre Azevedo)
* Add support for application/octet-stream (Ion Lupascu, Chris Scribner)
* Use os.tmpdir() to get tmp directory (Andrew Kelley)
* Improve package.json (Andrew Kelley, Sven Lito)
* Fix benchmark script (Andrew Kelley)
* Fix scope issue in incoming_forms (Sven Lito)
* Fix file handle leak on error (OrangeDog)
---
[First commit, #3270eb4b1f8b (May 4th, 2010)](https://github.com/node-formidable/formidable/commit/3270eb4b1f8bb667b8c12f64c36a4e7b854216d8)
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2011-present Felix Geisendörfer, and contributors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# formidable [![npm version][npmv-img]][npmv-url] [![MIT license][license-img]][license-url] [![Libera Manifesto][libera-manifesto-img]][libera-manifesto-url] [![Twitter][twitter-img]][twitter-url]
> A Node.js module for parsing form data, especially file uploads.
[![Code style][codestyle-img]][codestyle-url]
[![linux build status][linux-build-img]][build-url]
[![macos build status][macos-build-img]][build-url]
If you have any _how-to_ kind of questions, please read the [Contributing
Guide][contributing-url] and [Code of Conduct][code_of_conduct-url]
documents. For bugs reports and feature requests, [please create an
issue][open-issue-url] or ping [@wgw_eth / @wgw_lol][twitter-url]
at Twitter.
[![Conventional Commits][ccommits-img]][ccommits-url]
[![Minimum Required Nodejs][nodejs-img]][npmv-url]
[![Buy me a Kofi][kofi-img]][kofi-url]
[![Make A Pull Request][prs-welcome-img]][prs-welcome-url]
This project is [semantically versioned](https://semver.org) and if you want support in migrating between versions you can schedule us for training or support us through donations, so we can prioritize.
> [!CAUTION]
> As of April 2025, old versions like v1 and v2 are still the most used, while they are deprecated for years -- they are also vulnerable to attacks if you are not implementing it properly. **Please upgrade!** We are here to help, and AI Editors & Agents could help a lot in such codemod-like migrations.
> [!TIP]
> If you are starting a fresh project, you can check out the `formidable-mini` which is a super minimal version of Formidable (not quite configurable yet, but when it does it could become the basis for `formidable@v4`), using web standards like FormData API and File API, and you can use it to stream uploads directly to S3 or other such services.
[![][npm-weekly-img]][npmv-url] [![][npm-monthly-img]][npmv-url]
[![][npm-yearly-img]][npmv-url] [![][npm-alltime-img]][npmv-url]
## Project Status: Maintained
> [!NOTE]
> Check [VERSION NOTES](https://github.com/node-formidable/formidable/blob/master/VERSION_NOTES.md) for more information on v1, v2, and v3 plans, NPM dist-tags and branches._
This module was initially developed by
[**@felixge**](https://github.com/felixge) for
[Transloadit](http://transloadit.com/), a service focused on uploading and
encoding images and videos. It has been battle-tested against hundreds of GBs of
file uploads from a large variety of clients and is considered production-ready
and is used in production for years.
Currently, we are few maintainers trying to deal with it. :) More contributors
are always welcome! :heart: Jump on
[issue #412](https://github.com/felixge/node-formidable/issues/412) which is
closed, but if you are interested we can discuss it and add you after strict
rules, like enabling Two-Factor Auth in your npm and GitHub accounts.
## Highlights
- [Fast (~900-2500 mb/sec)](#benchmarks) & streaming multipart parser
- Automatically writing file uploads to disk (optional, see
[`options.fileWriteStreamHandler`](#options))
- [Plugins API](#useplugin-plugin) - allowing custom parsers and plugins
- Low memory footprint
- Graceful error handling
- Very high test coverage
## Install
This package is a dual ESM/commonjs package.
> [!NOTE]
> This project requires `Node.js >= 20`. Install it using [yarn](https://yarnpkg.com) or [npm](https://npmjs.com). _We highly recommend to use Yarn when you think to contribute to this project._
This is a low-level package, and if you're using a high-level framework it _may_
already be included. Check the examples below and the [examples/](https://github.com/node-formidable/formidable/tree/master/examples) folder.
```
# v2
npm install formidable@v2
# v3
npm install formidable
npm install formidable@v3
```
_**Note:** Future not ready releases will be published on `*-next` dist-tags for the corresponding version._
## Examples
For more examples look at the `examples/` directory.
### with Node.js http module
Parse an incoming file upload, with the
[Node.js's built-in `http` module](https://nodejs.org/api/http.html).
```js
import http from 'node:http';
import formidable, {errors as formidableErrors} from 'formidable';
const server = http.createServer(async (req, res) => {
if (req.url === '/api/upload' && req.method.toLowerCase() === 'post') {
// parse a file upload
const form = formidable({});
let fields;
let files;
try {
[fields, files] = await form.parse(req);
} catch (err) {
// example to check for a very specific error
if (err.code === formidableErrors.maxFieldsExceeded) {
}
console.error(err);
res.writeHead(err.httpCode || 400, { 'Content-Type': 'text/plain' });
res.end(String(err));
return;
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ fields, files }, null, 2));
return;
}
// show a file upload form
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(`
With Node.js "http" module
`);
});
server.listen(8080, () => {
console.log('Server listening on http://localhost:8080/ ...');
});
```
### with Express.js
There are multiple variants to do this, but Formidable just need Node.js Request
stream, so something like the following example should work just fine, without
any third-party [Express.js](https://ghub.now.sh/express) middleware.
Or try the
[examples/with-express.js](https://github.com/node-formidable/formidable/blob/master/examples/with-express.js)
```js
import express from 'express';
import formidable from 'formidable';
const app = express();
app.get('/', (req, res) => {
res.send(`
With "express" npm package
`);
});
app.post('/api/upload', (req, res, next) => {
const form = formidable({});
form.parse(req, (err, fields, files) => {
if (err) {
next(err);
return;
}
res.json({ fields, files });
});
});
app.listen(3000, () => {
console.log('Server listening on http://localhost:3000 ...');
});
```
### with Koa and Formidable
Of course, with [Koa v1, v2 or future v3](https://ghub.now.sh/koa) the things
are very similar. You can use `formidable` manually as shown below or through
the [koa-better-body](https://ghub.now.sh/koa-better-body) package which is
using `formidable` under the hood and support more features and different
request bodies, check its documentation for more info.
_Note: this example is assuming Koa v2. Be aware that you should pass `ctx.req`
which is Node.js's Request, and **NOT** the `ctx.request` which is Koa's Request
object - there is a difference._
```js
import Koa from 'Koa';
import formidable from 'formidable';
const app = new Koa();
app.on('error', (err) => {
console.error('server error', err);
});
app.use(async (ctx, next) => {
if (ctx.url === '/api/upload' && ctx.method.toLowerCase() === 'post') {
const form = formidable({});
// not very elegant, but that's for now if you don't want to use `koa-better-body`
// or other middlewares.
await new Promise((resolve, reject) => {
form.parse(ctx.req, (err, fields, files) => {
if (err) {
reject(err);
return;
}
ctx.set('Content-Type', 'application/json');
ctx.status = 200;
ctx.state = { fields, files };
ctx.body = JSON.stringify(ctx.state, null, 2);
resolve();
});
});
await next();
return;
}
// show a file upload form
ctx.set('Content-Type', 'text/html');
ctx.status = 200;
ctx.body = `
With "koa" npm package
`;
});
app.use((ctx) => {
console.log('The next middleware is called');
console.log('Results:', ctx.state);
});
app.listen(3000, () => {
console.log('Server listening on http://localhost:3000 ...');
});
```
## Benchmarks
The benchmark is quite old, from the old codebase. But maybe quite true though.
Previously the numbers was around ~500 mb/sec. Currently with moving to the new
Node.js Streams API it's faster. You can clearly see the differences between the
Node versions.
_Note: a lot better benchmarking could and should be done in future._
Benchmarked on 8GB RAM, Xeon X3440 (2.53 GHz, 4 cores, 8 threads)
```
~/github/node-formidable master
❯ nve --parallel 8 10 12 13 node benchmark/bench-multipart-parser.js
⬢ Node 8
1261.08 mb/sec
⬢ Node 10
1113.04 mb/sec
⬢ Node 12
2107.00 mb/sec
⬢ Node 13
2566.42 mb/sec
```

## API
### Formidable / IncomingForm
All shown are equivalent.
_Please pass [`options`](#options) to the function/constructor, not by assigning
them to the instance `form`_
```js
import formidable from 'formidable';
const form = formidable(options);
```
### Options
See it's defaults in [src/Formidable.js DEFAULT_OPTIONS](./src/Formidable.js)
(the `DEFAULT_OPTIONS` constant).
- `options.encoding` **{string}** - default `'utf-8'`; sets encoding for
incoming form fields,
- `options.uploadDir` **{string}** - default `os.tmpdir()`; the directory for
placing file uploads in. You can move them later by using `fs.rename()`.
- `options.keepExtensions` **{boolean}** - default `false`; to include the
extensions of the original files or not
- `options.allowEmptyFiles` **{boolean}** - default `false`; allow upload empty
files
- `options.minFileSize` **{number}** - default `1` (1byte); the minium size of
uploaded file.
- `options.maxFiles` **{number}** - default `Infinity`;
limit the amount of uploaded files, set Infinity for unlimited
- `options.maxFileSize` **{number}** - default `200 * 1024 * 1024` (200mb);
limit the size of each uploaded file.
- `options.maxTotalFileSize` **{number}** - default `options.maxFileSize`;
limit the size of the batch of uploaded files.
- `options.maxFields` **{number}** - default `1000`; limit the number of fields, set Infinity for unlimited
- `options.maxFieldsSize` **{number}** - default `20 * 1024 * 1024` (20mb);
limit the amount of memory all fields together (except files) can allocate in
bytes.
- `options.hashAlgorithm` **{string | false}** - default `false`; include checksums calculated
for incoming files, set this to some hash algorithm, see
[crypto.createHash](https://nodejs.org/api/crypto.html#crypto_crypto_createhash_algorithm_options)
for available algorithms
- `options.fileWriteStreamHandler` **{function}** - default `null`, which by
default writes to host machine file system every file parsed; The function
should return an instance of a
[Writable stream](https://nodejs.org/api/stream.html#stream_class_stream_writable)
that will receive the uploaded file data. With this option, you can have any
custom behavior regarding where the uploaded file data will be streamed for.
If you are looking to write the file uploaded in other types of cloud storages
(AWS S3, Azure blob storage, Google cloud storage) or private file storage,
this is the option you're looking for. When this option is defined the default
behavior of writing the file in the host machine file system is lost.
- `options.filename` **{function}** - default `undefined` Use it to control
newFilename. Must return a string. Will be joined with options.uploadDir.
- `options.filter` **{function}** - default function that always returns true.
Use it to filter files before they are uploaded. Must return a boolean. Will not make the form.parse error
- `options.createDirsFromUploads` **{boolean}** - default false. If true, makes direct folder uploads possible. Use ` ` to create a form to upload folders. Has to be used with the options `options.uploadDir` and `options.filename` where `options.filename` has to return a string with the character `/` for folders to be created. The base will be `options.uploadDir`.
#### `options.filename` **{function}** function (name, ext, part, form) -> string
where part can be decomposed as
```js
const { originalFilename, mimetype} = part;
```
_**Note:** If this size of combined fields, or size of some file is exceeded, an
`'error'` event is fired._
```js
// The amount of bytes received for this form so far.
form.bytesReceived;
```
```js
// The expected number of bytes in this form.
form.bytesExpected;
```
#### `options.filter` **{function}** function ({name, originalFilename, mimetype}) -> boolean
Behaves like Array.filter: Returning false will simply ignore the file and go to the next.
```js
const options = {
filter: function ({name, originalFilename, mimetype}) {
// keep only images
return mimetype && mimetype.includes("image");
}
};
```
**Note:** use an outside variable to cancel all uploads upon the first error
**Note:** use form.emit('error') to make form.parse error
```js
let cancelUploads = false;// create variable at the same scope as form
const options = {
filter: function ({name, originalFilename, mimetype}) {
// keep only images
const valid = mimetype && mimetype.includes("image");
if (!valid) {
form.emit('error', new formidableErrors.default('invalid type', 0, 400)); // optional make form.parse error
cancelUploads = true; //variable to make filter return false after the first problem
}
return valid && !cancelUploads;
}
};
```
### .parse(request, ?callback)
Parses an incoming Node.js `request` containing form data. If `callback` is not provided a promise is returned.
```js
const form = formidable({ uploadDir: __dirname });
form.parse(req, (err, fields, files) => {
console.log('fields:', fields);
console.log('files:', files);
});
// with Promise
const [fields, files] = await form.parse(req);
```
You may overwrite this method if you are interested in directly accessing the
multipart stream. Doing so will disable any `'field'` / `'file'` events
processing which would occur otherwise, making you fully responsible for
handling the processing.
About `uploadDir`, given the following directory structure
```
project-name
├── src
│ └── server.js
│
└── uploads
└── image.jpg
```
`__dirname` would be the same directory as the source file itself (src)
```js
`${__dirname}/../uploads`
```
to put files in uploads.
Omitting `__dirname` would make the path relative to the current working directory. This would be the same if server.js is launched from src but not project-name.
`null` will use default which is `os.tmpdir()`
Note: If the directory does not exist, the uploaded files are __silently discarded__. To make sure it exists:
```js
import {createNecessaryDirectoriesSync} from "filesac";
const uploadPath = `${__dirname}/../uploads`;
createNecessaryDirectoriesSync(`${uploadPath}/x`);
```
In the example below, we listen on couple of events and direct them to the
`data` listener, so you can do whatever you choose there, based on whether its
before the file been emitted, the header value, the header name, on field, on
file and etc.
Or the other way could be to just override the `form.onPart` as it's shown a bit
later.
```js
form.once('error', console.error);
form.on('fileBegin', (formname, file) => {
form.emit('data', { name: 'fileBegin', formname, value: file });
});
form.on('file', (formname, file) => {
form.emit('data', { name: 'file', formname, value: file });
});
form.on('field', (fieldName, fieldValue) => {
form.emit('data', { name: 'field', key: fieldName, value: fieldValue });
});
form.once('end', () => {
console.log('Done!');
});
// If you want to customize whatever you want...
form.on('data', ({ name, key, value, buffer, start, end, formname, ...more }) => {
if (name === 'partBegin') {
}
if (name === 'partData') {
}
if (name === 'headerField') {
}
if (name === 'headerValue') {
}
if (name === 'headerEnd') {
}
if (name === 'headersEnd') {
}
if (name === 'field') {
console.log('field name:', key);
console.log('field value:', value);
}
if (name === 'file') {
console.log('file:', formname, value);
}
if (name === 'fileBegin') {
console.log('fileBegin:', formname, value);
}
});
```
### .use(plugin: Plugin)
A method that allows you to extend the Formidable library. By default we include
4 plugins, which essentially are adapters to plug the different built-in parsers.
**The plugins added by this method are always enabled.**
_See [src/plugins/](./src/plugins/) for more detailed look on default plugins._
The `plugin` param has such signature:
```typescript
function(formidable: Formidable, options: Options): void;
```
The architecture is simple. The `plugin` is a function that is passed with the
Formidable instance (the `form` across the README examples) and the options.
**Note:** the plugin function's `this` context is also the same instance.
```js
const form = formidable({ keepExtensions: true });
form.use((self, options) => {
// self === this === form
console.log('woohoo, custom plugin');
// do your stuff; check `src/plugins` for inspiration
});
form.parse(req, (error, fields, files) => {
console.log('done!');
});
```
**Important to note**, is that inside plugin `this.options`, `self.options` and
`options` MAY or MAY NOT be the same. General best practice is to always use the
`this`, so you can later test your plugin independently and more easily.
If you want to disable some parsing capabilities of Formidable, you can disable
the plugin which corresponds to the parser. For example, if you want to disable
multipart parsing (so the [src/parsers/Multipart.js](./src/parsers/Multipart.js)
which is used in [src/plugins/multipart.js](./src/plugins/multipart.js)), then
you can remove it from the `options.enabledPlugins`, like so
```js
import formidable, {octetstream, querystring, json} from "formidable";
const form = formidable({
hashAlgorithm: 'sha1',
enabledPlugins: [octetstream, querystring, json],
});
```
**Be aware** that the order _MAY_ be important too. The names corresponds 1:1 to
files in [src/plugins/](./src/plugins) folder.
Pull requests for new built-in plugins MAY be accepted - for example, more
advanced querystring parser. Add your plugin as a new file in `src/plugins/`
folder (lowercased) and follow how the other plugins are made.
### form.onPart
If you want to use Formidable to only handle certain parts for you, you can do
something similar. Or see
[#387](https://github.com/node-formidable/node-formidable/issues/387) for
inspiration, you can for example validate the mime-type.
```js
const form = formidable();
form.onPart = (part) => {
part.on('data', (buffer) => {
// do whatever you want here
});
};
```
For example, force Formidable to be used only on non-file "parts" (i.e., html
fields)
```js
const form = formidable();
form.onPart = function (part) {
// let formidable handle only non-file parts
if (part.originalFilename === '' || !part.mimetype) {
// used internally, please do not override!
form._handlePart(part);
}
};
```
### File
```ts
export interface File {
// The size of the uploaded file in bytes.
// If the file is still being uploaded (see `'fileBegin'` event),
// this property says how many bytes of the file have been written to disk yet.
file.size: number;
// The path this file is being written to. You can modify this in the `'fileBegin'` event in
// case you are unhappy with the way formidable generates a temporary path for your files.
file.filepath: string;
// The name this file had according to the uploading client.
file.originalFilename: string | null;
// calculated based on options provided
file.newFilename: string | null;
// The mime type of this file, according to the uploading client.
file.mimetype: string | null;
// A Date object (or `null`) containing the time this file was last written to.
// Mostly here for compatibility with the [W3C File API Draft](http://dev.w3.org/2006/webapi/FileAPI/).
file.mtime: Date | null;
file.hashAlgorithm: false | |'sha1' | 'md5' | 'sha256'
// If `options.hashAlgorithm` calculation was set, you can read the hex digest out of this var (at the end it will be a string)
file.hash: string | object | null;
}
```
#### file.toJSON()
This method returns a JSON-representation of the file, allowing you to
`JSON.stringify()` the file which is useful for logging and responding to
requests.
### Events
#### `'progress'`
Emitted after each incoming chunk of data that has been parsed. Can be used to
roll your own progress bar. **Warning** Use this only for server side progress bar. On the client side better use `XMLHttpRequest` with `xhr.upload.onprogress =`
```js
form.on('progress', (bytesReceived, bytesExpected) => {});
```
#### `'field'`
Emitted whenever a field / value pair has been received.
```js
form.on('field', (name, value) => {});
```
#### `'fileBegin'`
Emitted whenever a new file is detected in the upload stream. Use this event if
you want to stream the file to somewhere else while buffering the upload on the
file system.
```js
form.on('fileBegin', (formName, file) => {
// accessible here
// formName the name in the form ( ) or http filename for octetstream
// file.originalFilename http filename or null if there was a parsing error
// file.newFilename generated hexoid or what options.filename returned
// file.filepath default pathname as per options.uploadDir and options.filename
// file.filepath = CUSTOM_PATH // to change the final path
});
```
#### `'file'`
Emitted whenever a field / file pair has been received. `file` is an instance of
`File`.
```js
form.on('file', (formname, file) => {
// same as fileBegin, except
// it is too late to change file.filepath
// file.hash is available if options.hash was used
});
```
#### `'error'`
Emitted when there is an error processing the incoming form. A request that
experiences an error is automatically paused, you will have to manually call
`request.resume()` if you want the request to continue firing `'data'` events.
May have `error.httpCode` and `error.code` attached.
```js
form.on('error', (err) => {});
```
#### `'aborted'`
Emitted when the request was aborted by the user. Right now this can be due to a
'timeout' or 'close' event on the socket. After this event is emitted, an
`error` event will follow. In the future there will be a separate 'timeout'
event (needs a change in the node core).
```js
form.on('aborted', () => {});
```
#### `'end'`
Emitted when the entire request has been received, and all contained files have
finished flushing to disk. This is a great place for you to send your response.
```js
form.on('end', () => {});
```
### Helpers
#### firstValues
Gets first values of fields, like pre 3.0.0 without multiples pass in a list of optional exceptions where arrays of strings is still wanted (`` for example)
```js
import { firstValues } from 'formidable/src/helpers/firstValues.js';
// ...
form.parse(request, async (error, fieldsMultiple, files) => {
if (error) {
//...
}
const exceptions = ['thisshouldbeanarray'];
const fieldsSingle = firstValues(form, fieldsMultiple, exceptions);
// ...
```
#### readBooleans
Html form input type="checkbox" only send the value "on" if checked,
convert it to booleans for each input that is expected to be sent as a checkbox, only use after firstValues or similar was called.
```js
import { firstValues } from 'formidable/src/helpers/firstValues.js';
import { readBooleans } from 'formidable/src/helpers/readBooleans.js';
// ...
form.parse(request, async (error, fieldsMultiple, files) => {
if (error) {
//...
}
const fieldsSingle = firstValues(form, fieldsMultiple);
const expectedBooleans = ['checkbox1', 'wantsNewsLetter', 'hasACar'];
const fieldsWithBooleans = readBooleans(fieldsSingle, expectedBooleans);
// ...
```
## Changelog
[./CHANGELOG.md](./CHANGELOG.md)
## Ports & Credits
- [multipart-parser](http://github.com/FooBarWidget/multipart-parser): a C++
parser based on formidable
- [Ryan Dahl](https://x.com/rough__sea) for his work on
[http-parser](http://github.com/ry/http-parser) which heavily inspired the
initial `multipart_parser.js`.
## Contributing
If the documentation is unclear or has a typo, please click on the page's `Edit`
button (pencil icon) and suggest a correction. If you would like to help us fix
a bug or add a new feature, please check our [Contributing
Guide][contributing-url]. Pull requests are welcome!
Thanks goes to these wonderful people
([emoji key](https://allcontributors.org/docs/en/emoji-key)):
From a [Felix blog post](https://felixge.de/2013/03/11/the-pull-request-hack/):
- [Sven Lito](https://github.com/svnlto) for fixing bugs and merging patches
- [egirshov](https://github.com/egirshov) for contributing many improvements to the node-formidable multipart parser
- [Andrew Kelley](https://github.com/superjoe30) for also helping with fixing bugs and making improvements
- [Mike Frey](https://github.com/mikefrey) for contributing JSON support
## License
Formidable is licensed under the [MIT License][license-url].
[codestyle-url]: https://github.com/airbnb/javascript
[codestyle-img]: https://badgen.net/badge/code%20style/airbnb%20%2B%20prettier/ff5a5f?icon=airbnb&cache=300
[codecov-url]: https://codecov.io/gh/node-formidable/formidable
[codecov-img]: https://badgen.net/codecov/c/github/node-formidable/formidable/master?icon=codecov
[npmv-canary-img]: https://badgen.net/npm/v/formidable/canary?icon=npm
[npmv-dev-img]: https://badgen.net/npm/v/formidable/dev?icon=npm
[npmv-img]: https://badgen.net/npm/v/formidable?icon=npm
[npmv-url]: https://npmjs.com/package/formidable
[license-img]: https://badgen.net/npm/license/formidable
[license-url]: https://github.com/node-formidable/formidable/blob/master/LICENSE
[chat-img]: https://badgen.net/badge/chat/on%20gitter/46BC99?icon=gitter
[chat-url]: https://gitter.im/node-formidable/Lobby
[libera-manifesto-url]: https://liberamanifesto.com
[libera-manifesto-img]: https://badgen.net/badge/libera/manifesto/grey
[renovateapp-url]: https://renovatebot.com
[renovateapp-img]: https://badgen.net/badge/renovate/enabled/green?cache=300
[prs-welcome-img]: https://badgen.net/badge/PRs/welcome/green?cache=300
[prs-welcome-url]: http://makeapullrequest.com
[twitter-url]: https://twitter.com/wgw_eth
[twitter-img]: https://badgen.net/badge/twitter/follow/wgw_eth?icon=twitter&color=1da1f2&cache=30
[npm-weekly-img]: https://badgen.net/npm/dw/formidable?icon=npm&cache=300
[npm-monthly-img]: https://badgen.net/npm/dm/formidable?icon=npm&cache=300
[npm-yearly-img]: https://badgen.net/npm/dy/formidable?icon=npm&cache=300
[npm-alltime-img]: https://badgen.net/npm/dt/formidable?icon=npm&cache=300&label=total%20downloads
[nodejs-img]: https://badgen.net/badge/node/>=%2010.13/green?cache=300
[ccommits-url]: https://conventionalcommits.org/
[ccommits-img]: https://badgen.net/badge/conventional%20commits/v1.0.0/green?cache=300
[contributing-url]: https://github.com/node-formidable/.github/blob/master/CONTRIBUTING.md
[code_of_conduct-url]: https://github.com/node-formidable/.github/blob/master/CODE_OF_CONDUCT.md
[open-issue-url]: https://github.com/node-formidable/formidable/issues/new
[tidelift-url]: https://tidelift.com/subscription/pkg/npm-formidable?utm_source=npm-formidable&utm_medium=referral&utm_campaign=enterprise
[tidelift-img]: https://badgen.net/badge/tidelift/subscription/4B5168?labelColor=F6914D
[kofi-url]: https://ko-fi.com/tunnckoCore/commissions
[kofi-img]: https://badgen.net/badge/ko-fi/support/29abe0c2?cache=300&icon=https://rawcdn.githack.com/tunnckoCore/badgen-icons/f8264c6414e0bec449dd86f2241d50a9b89a1203/icons/kofi.svg
[linux-build-img]: https://badgen.net/github/checks/node-formidable/formidable/master?label=linux%20build&icon=github
[macos-build-img]: https://badgen.net/github/checks/node-formidable/formidable/master?label=macos%20build&icon=github
[windows-build-img]: https://badgen.net/github/checks/node-formidable/formidable/master/windows?cache=300&label=windows%20build&icon=github
[build-url]: https://github.com/node-formidable/formidable/actions
================================================
FILE: README_pt_BR.md
================================================
# formidable [![npm version][npmv-img]][npmv-url] [![MIT license][license-img]][license-url] [![Libera Manifesto][libera-manifesto-img]][libera-manifesto-url] [![Twitter][twitter-img]][twitter-url]
> A Node.js module for parsing form data, especially file uploads.
[![Code style][codestyle-img]][codestyle-url]
[![codecoverage][codecov-img]][codecov-url]
[![linux build status][linux-build-img]][build-url]
[![macos build status][macos-build-img]][build-url]
Se você tiver qualquer tipo de pergunta sobre _como_ fazer, por favor leia o [Contributing
Guia][contributing-url] e [Código de Conduta][code_of_conduct-url]
documentos. Para relatórios de bugs e solicitações de recursos, [crie uma
issue][open-issue-url] ou ping [@tunnckoCore / @3a1FcBx0](https://twitter.com/3a1FcBx0)
no Twitter.
[![Conventional Commits][ccommits-img]][ccommits-url]
[![Minimum Required Nodejs][nodejs-img]][npmv-url]
[![Tidelift Subscription][tidelift-img]][tidelift-url]
[![Buy me a Kofi][kofi-img]][kofi-url]
[![Renovate App Status][renovateapp-img]][renovateapp-url]
[![Make A Pull Request][prs-welcome-img]][prs-welcome-url]
Este projeto é [semanticamente versionado](https://semver.org) e está disponível como
parte da [Assinatura Tidelift][tidelift-url] para nível profissional
garantias, suporte aprimorado e segurança.
[Saiba mais.](https://tidelift.com/subscription/pkg/npm-formidable?utm_source=npm-formidable&utm_medium=referral&utm_campaign=enterprise)
_Os mantenedores do `formidable` e milhares de outros pacotes estão trabalhando
com Tidelift para fornecer suporte comercial e manutenção para o Open Source
dependências que você usa para construir seus aplicativos. Economize tempo, reduza riscos e
melhorar a integridade do código, enquanto paga aos mantenedores das dependências exatas que você
usar._
[![][npm-weekly-img]][npmv-url] [![][npm-monthly-img]][npmv-url]
[![][npm-yearly-img]][npmv-url] [![][npm-alltime-img]][npmv-url]
## Status do Projeto: Mantido
_Verifique [VERSION NOTES](https://github.com/node-formidable/formidable/blob/master/VERSION_NOTES.md) para obter mais informações sobre os planos v1, v2 e v3, NPM dist-tags e branches._
Este módulo foi inicialmente desenvolvido por
[**@felixge**](https://github.com/felixge) para
[Transloadit](http://transloadit.com/), um serviço focado em upload e
codificação de imagens e vídeos. Foi testado em batalha contra centenas de GBs de
uploads de arquivos de uma grande variedade de clientes e é considerado pronto para produção
e é usado na produção por anos.
Atualmente, somos poucos mantenedores tentando lidar com isso. :) Mais contribuidores
são sempre bem-vindos! ❤️ Pule
[issue #412](https://github.com/felixge/node-formidable/issues/412) que está
fechado, mas se você estiver interessado, podemos discuti-lo e adicioná-lo após regras estritas, como ativar o Two-Factor Auth em suas contas npm e GitHub.
## Destaques
- [Rápido (~ 900-2500 mb/seg)](#benchmarks) e analisador multiparte de streaming
- Gravar uploads de arquivos automaticamente no disco (opcional, consulte
[`options.fileWriteStreamHandler`](#options))
- [API de plug-ins](#useplugin-plugin) - permitindo analisadores e plug-ins personalizados
- Baixo consumo de memória
- Tratamento de erros gracioso
- Cobertura de teste muito alta
## Instalar
Este projeto requer `Node.js >= 10.13`. Instale-o usando
[yarn](https://yarnpkg.com) ou [npm](https://npmjs.com). _Nós altamente
recomendamos usar o Yarn quando pensar em contribuir para este projeto._
Este é um pacote de baixo nível e, se você estiver usando uma estrutura de alto nível, _pode_ já estar incluído. Verifique os exemplos
abaixo e a pasta [examples/](https://github.com/node-formidable/formidable/tree/master/examples).
```
# v2
npm install formidable
npm install formidable@v2
# v3
npm install formidable@v3
```
_**Nota:** Em um futuro próximo, a v3 será publicada na dist-tag `latest` do NPM.
Versões futuras não prontas serão publicadas nas dist-tags `*-next` para a versão correspondente._
## Exemplos
Para mais exemplos veja o diretório `examples/`.
### com módulo http Node.js
Analisar um upload de arquivo de entrada, com o
[Módulo `http` integrado do Node.js](https://nodejs.org/api/http.html).
```js
import http from 'node:http';
import formidable, {errors as formidableErrors} from 'formidable';
const server = http.createServer((req, res) => {
if (req.url === '/api/upload' && req.method.toLowerCase() === 'post') {
// analisar um upload de arquivo
const form = formidable({});
form.parse(req, (err, fields, files) => {
if (err) {
// exemplo para verificar um erro muito específico
if (err.code === formidableErrors.maxFieldsExceeded) {
}
res.writeHead(err.httpCode || 400, { 'Content-Type': 'text/plain' });
res.end(String(err));
return;
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ fields, files }, null, 2));
});
return;
}
// mostrar um formulário de upload de arquivo
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(`
With Node.js "http" module
`);
});
server.listen(8080, () => {
console.log('Server listening on http://localhost:8080/ ...');
});
```
### com Express.js
Existem várias variantes para fazer isso, mas o Formidable só precisa do Node.js Request
stream, então algo como o exemplo a seguir deve funcionar bem, sem nenhum middleware [Express.js](https://ghub.now.sh/express) de terceiros.
Ou tente o
[examples/with-express.js](https://github.com/node-formidable/formidable/blob/master/examples/with-express.js)
```js
import express from 'express';
import formidable from 'formidable';
const app = express();
app.get('/', (req, res) => {
res.send(`
With "express" npm package
`);
});
app.post('/api/upload', (req, res, next) => {
const form = formidable({});
form.parse(req, (err, fields, files) => {
if (err) {
next(err);
return;
}
res.json({ fields, files });
});
});
app.listen(3000, () => {
console.log('Server listening on http://localhost:3000 ...');
});
```
### com Koa e Formidable
Claro, com [Koa v1, v2 ou future v3](https://ghub.now.sh/koa) as coisas
sao muito parecidas. Você pode usar `formidable` manualmente como mostrado abaixo ou através
do pacote [koa-better-body](https://ghub.now.sh/koa-better-body) que é
usando `formidable` sob o capô e suporte a mais recursos e diferentes
corpos de solicitação, verifique sua documentação para mais informações.
_Nota: este exemplo está assumindo Koa v2. Esteja ciente de que você deve passar `ctx.req`
que é a solicitação do Node.js e **NÃO** o `ctx.request` que é a solicitação do Koa
objeto - há uma diferença._
```js
import Koa from 'Koa';
import formidable from 'formidable';
const app = new Koa();
app.on('error', (err) => {
console.error('server error', err);
});
app.use(async (ctx, next) => {
if (ctx.url === '/api/upload' && ctx.method.toLowerCase() === 'post') {
const form = formidable({});
// não muito elegante, mas é por enquanto se você não quiser usar `koa-better-body`
// ou outros middlewares.
await new Promise((resolve, reject) => {
form.parse(ctx.req, (err, fields, files) => {
if (err) {
reject(err);
return;
}
ctx.set('Content-Type', 'application/json');
ctx.status = 200;
ctx.state = { fields, files };
ctx.body = JSON.stringify(ctx.state, null, 2);
resolve();
});
});
await next();
return;
}
// mostrar um formulário de upload de arquivo
ctx.set('Content-Type', 'text/html');
ctx.status = 200;
ctx.body = `
With "koa" npm package
`;
});
app.use((ctx) => {
console.log('The next middleware is called');
console.log('Results:', ctx.state);
});
app.listen(3000, () => {
console.log('Server listening on http://localhost:3000 ...');
});
```
## Benchmarks
O benchmark é bastante antigo, da antiga base de código. Mas talvez seja bem verdade.
Anteriormente, os números giravam em torno de ~ 500 mb/s. Atualmente com a mudança para o novo
Node.js Streams API, é mais rápido. Você pode ver claramente as diferenças entre as
versões do Node.
_Observação: um benchmarking muito melhor pode e deve ser feito no futuro._
Benchmark realizado em 8 GB de RAM, Xeon X3440 (2,53 GHz, 4 núcleos, 8 threads)
```
~/github/node-formidable master
❯ nve --parallel 8 10 12 13 node benchmark/bench-multipart-parser.js
⬢ Node 8
1261.08 mb/sec
⬢ Node 10
1113.04 mb/sec
⬢ Node 12
2107.00 mb/sec
⬢ Node 13
2566.42 mb/sec
```

## API
### Formidable / IncomingForm
Todos os mostrados são equivalentes.
_Por favor, passe [`options`](#options) para a função/construtor, não atribuindo
eles para a instância `form`_
```js
import formidable from 'formidable';
const form = formidable(options);
```
### Opções
Veja seus padrões em [src/Formidable.js DEFAULT_OPTIONS](./src/Formidable.js)
(a constante `DEFAULT_OPTIONS`).
- `options.encoding` **{string}** - padrão `'utf-8'`; define a codificação para campos de formulário de entrada,
- `options.uploadDir` **{string}** - padrão `os.tmpdir()`; o diretório para colocar os uploads de arquivos. Você pode movê-los mais tarde usando `fs.rename()`.
- `options.keepExtensions` **{boolean}** - padrão `false`; incluir as extensões dos arquivos originais ou não
- `options.allowEmptyFiles` **{boolean}** - padrão `false`; permitir upload de arquivos vazios
- `options.minFileSize` **{number}** - padrão `1` (1byte); o tamanho mínimo do arquivo carregado.
- `options.maxFiles` **{number}** - padrão `Infinity`;
limitar a quantidade de arquivos carregados, defina Infinity para ilimitado
- `options.maxFileSize` **{number}** - padrão `200 * 1024 * 1024` (200mb);
limitar o tamanho de cada arquivo carregado.
- `options.maxTotalFileSize` **{number}** - padrão `options.maxFileSize`;
limitar o tamanho do lote de arquivos carregados.
- `options.maxFields` **{number}** - padrão `1000`; limite o número de campos, defina Infinity para ilimitado
- `options.maxFieldsSize` **{number}** - padrão `20 * 1024 * 1024` (20mb);
limitar a quantidade de memória que todos os campos juntos (exceto arquivos) podem alocar em
bytes.
- `options.hashAlgorithm` **{string | false}** - padrão `false`; incluir checksums calculados
para arquivos recebidos, defina isso para algum algoritmo de hash, consulte
[crypto.createHash](https://nodejs.org/api/crypto.html#crypto_crypto_createhash_algorithm_options)
para algoritmos disponíveis
- `options.fileWriteStreamHandler` **{function}** - padrão `null`, que por padrão grava no sistema de arquivos da máquina host cada arquivo analisado; A função
deve retornar uma instância de um
[fluxo gravável](https://nodejs.org/api/stream.html#stream_class_stream_writable)
que receberá os dados do arquivo carregado. Com esta opção, você pode ter qualquer
comportamento personalizado em relação a onde os dados do arquivo carregado serão transmitidos.
Se você deseja gravar o arquivo carregado em outros tipos de armazenamento em nuvem
(AWS S3, armazenamento de blob do Azure, armazenamento em nuvem do Google) ou armazenamento de arquivo privado,
esta é a opção que você está procurando. Quando esta opção é definida, o comportamento padrão de gravar o arquivo no sistema de arquivos da máquina host é perdido.
- `options.filename` **{function}** - padrão `undefined` Use-o para controlar newFilename. Deve retornar uma string. Será associado a options.uploadDir.
- `options.filter` **{function}** - função padrão que sempre retorna verdadeiro.
Use-o para filtrar arquivos antes de serem carregados. Deve retornar um booleano.
#### `options.filename` **{function}** function (name, ext, part, form) -> string
onde a parte pode ser decomposta como
```js
const { originalFilename, mimetype} = part;
```
_**Observação:** Se este tamanho de campos combinados, ou tamanho de algum arquivo for excedido, um
O evento `'error'` é disparado._
```js
// A quantidade de bytes recebidos para este formulário até agora.
form.bytesReceived;
```
```js
// O número esperado de bytes neste formulário.
form.bytesExpected;
```
#### `options.filter` **{function}** function ({name, originalFilename, mimetype}) -> boolean
**Observação:** use uma variável externa para cancelar todos os uploads no primeiro erro
```js
const options = {
filter: function ({name, originalFilename, mimetype}) {
// manter apenas imagens
return mimetype && mimetype.includes("image");
}
};
```
### .parse(request, callback)
Analisa uma `request` do Node.js recebida contendo dados de formulário. Se `callback` for
fornecido, todos os campos e arquivos são coletados e passados para o retorno de chamada.
```js
const form = formidable({ uploadDir: __dirname });
form.parse(req, (err, fields, files) => {
console.log('fields:', fields);
console.log('files:', files);
});
```
Você pode substituir esse método se estiver interessado em acessar diretamente o
fluxo de várias partes. Fazer isso desativará qualquer processamento de eventos `'field'` / `'file'`
que ocorreria de outra forma, tornando você totalmente responsável por lidar com o processamento.
Sobre `uploadDir`, dada a seguinte estrutura de diretório
```
project-name
├── src
│ └── server.js
│
└── uploads
└── image.jpg
```
`__dirname` seria o mesmo diretório que o próprio arquivo de origem (src)
```js
`${__dirname}/../uploads`
```
para colocar arquivos em uploads.
Omitir `__dirname` tornaria o caminho relativo ao diretório de trabalho atual. Isso seria o mesmo se server.js fosse iniciado a partir de src, mas não de project-name.
`null` usará o padrão que é `os.tmpdir()`
Nota: Se o diretório não existir, os arquivos carregados são __silenciosamente descartados__. Para ter certeza de que existe:
```js
import {createNecessaryDirectoriesSync} from "filesac";
const uploadPath = `${__dirname}/../uploads`;
createNecessaryDirectoriesSync(`${uploadPath}/x`);
```
No exemplo abaixo, escutamos alguns eventos e os direcionamos para o ouvinte `data`, para
que você possa fazer o que quiser lá, com base em se é antes do arquivo ser emitido, o valor do
cabeçalho, o nome do cabeçalho, no campo , em arquivo e etc.
Ou a outra maneira poderia ser apenas substituir o `form.onPart` como é mostrado um pouco
mais tarde.
```js
form.once('error', console.error);
form.on('fileBegin', (formname, file) => {
form.emit('data', { name: 'fileBegin', formname, value: file });
});
form.on('file', (formname, file) => {
form.emit('data', { name: 'file', formname, value: file });
});
form.on('field', (fieldName, fieldValue) => {
form.emit('data', { name: 'field', key: fieldName, value: fieldValue });
});
form.once('end', () => {
console.log('Done!');
});
// Se você quiser personalizar o que quiser...
form.on('data', ({ name, key, value, buffer, start, end, formname, ...more }) => {
if (name === 'partBegin') {
}
if (name === 'partData') {
}
if (name === 'headerField') {
}
if (name === 'headerValue') {
}
if (name === 'headerEnd') {
}
if (name === 'headersEnd') {
}
if (name === 'field') {
console.log('field name:', key);
console.log('field value:', value);
}
if (name === 'file') {
console.log('file:', formname, value);
}
if (name === 'fileBegin') {
console.log('fileBegin:', formname, value);
}
});
```
### .use(plugin: Plugin)
Um método que permite estender a biblioteca Formidable. Por padrão, incluímos
4 plug-ins, que são essencialmente adaptadores para conectar os diferentes analisadores integrados.
**Os plugins adicionados por este método estão sempre ativados.**
_Consulte [src/plugins/](./src/plugins/) para uma visão mais detalhada dos plug-ins padrão._
O parâmetro `plugin` tem essa assinatura:
```typescript
function(formidable: Formidable, options: Options): void;
```
A arquitetura é simples. O `plugin` é uma função que é passada com a instância Formidable (o `form` nos exemplos README) e as opções.
**Observação:** o contexto `this` da função do plug-in também é a mesma instância.
```js
const form = formidable({ keepExtensions: true });
form.use((self, options) => {
// self === this === form
console.log('woohoo, custom plugin');
// faça suas coisas; verifique `src/plugins` para inspiração
});
form.parse(req, (error, fields, files) => {
console.log('done!');
});
```
**Importante observar**, é que dentro do plugin `this.options`, `self.options` e
`options` PODEM ou NÃO ser iguais. A melhor prática geral é sempre usar o
`this`, para que você possa testar seu plugin mais tarde de forma independente e mais fácil.
Se você quiser desabilitar alguns recursos de análise do Formidable, você pode desabilitar
o plugin que corresponde ao analisador. Por exemplo, se você deseja desabilitar a análise de
várias partes (para que o [src/parsers/Multipart.js](./src/parsers/Multipart.js)
que é usado em [src/plugins/multipart.js](./src/plugins/multipart.js)), então
você pode removê-lo do `options.enabledPlugins`, assim
```js
import formidable, {octetstream, querystring, json} from "formidable";
const form = formidable({
hashAlgorithm: 'sha1',
enabledPlugins: [octetstream, querystring, json],
});
```
**Esteja ciente** de que a ordem _PODE_ ser importante também. Os nomes correspondem 1:1 a
arquivos na pasta [src/plugins/](./src/plugins).
Solicitações pull para novos plug-ins integrados PODEM ser aceitas - por exemplo, analisador de
querystring mais avançado. Adicione seu plugin como um novo arquivo na pasta `src/plugins/` (em letras minúsculas) e
siga como os outros plugins são feitos.
### form.onPart
Se você quiser usar Formidable para manipular apenas algumas partes para você, você pode fazer
alguma coisa similar. ou ver
[#387](https://github.com/node-formidable/node-formidable/issues/387) para
inspiração, você pode, por exemplo, validar o tipo mime.
```js
const form = formidable();
form.onPart = (part) => {
part.on('data', (buffer) => {
// faça o que quiser aqui
});
};
```
Por exemplo, force Formidable a ser usado apenas em "partes" que não sejam de arquivo (ou seja, html
Campos)
```js
const form = formidable();
form.onPart = function (part) {
// deixe formidável lidar apenas com partes não arquivadas
if (part.originalFilename === '' || !part.mimetype) {
// usado internamente, por favor, não substitua!
form._handlePart(part);
}
};
```
### Arquivo
```ts
export interface File {
// O tamanho do arquivo enviado em bytes.
// Se o arquivo ainda estiver sendo carregado (veja o evento `'fileBegin'`),
// esta propriedade diz quantos bytes do arquivo já foram gravados no disco.
file.size: number;
// O caminho em que este arquivo está sendo gravado. Você pode modificar isso no evento `'fileBegin'`
// caso você esteja insatisfeito com a forma como o formidable gera um caminho temporário para seus arquivos.
file.filepath: string;
// O nome que este arquivo tinha de acordo com o cliente de upload.
file.originalFilename: string | null;
// calculado com base nas opções fornecidas.
file.newFilename: string | null;
// O tipo mime deste arquivo, de acordo com o cliente de upload.
file.mimetype: string | null;
// Um objeto Date (ou `null`) contendo a hora em que este arquivo foi gravado pela última vez.
// Principalmente aqui para compatibilidade com o [W3C File API Draft](http://dev.w3.org/2006/webapi/FileAPI/).
file.mtime: Date | null;
file.hashAlgorithm: false | |'sha1' | 'md5' | 'sha256'
// Se o cálculo `options.hashAlgorithm` foi definido, você pode ler o resumo hexadecimal desta var (no final, será uma string)
file.hash: string | object | null;
}
```
#### file.toJSON()
Este método retorna uma representação JSON do arquivo, permitindo que você `JSON.stringify()`
o arquivo que é útil para registrar e responder a solicitações.
### Eventos
#### `'progress'`
Emitido após cada bloco de entrada de dados que foi analisado. Pode ser usado para rolar sua própria barra de progresso. **Aviso** Use isso
apenas para a barra de progresso do lado do servidor. No lado do cliente, é melhor usar `XMLHttpRequest` com `xhr.upload.onprogress =`
```js
form.on('progress', (bytesReceived, bytesExpected) => {});
```
#### `'field'`
Emitido sempre que um par campo/valor é recebido.
```js
form.on('field', (name, value) => {});
```
#### `'fileBegin'`
Emitido sempre que um novo arquivo é detectado no fluxo de upload.
Use este evento se desejar transmitir o arquivo para outro lugar enquanto armazena o upload no sistema de arquivos.
```js
form.on('fileBegin', (formName, file) => {
// acessível aqui
// formName o nome no formulário ( ) ou http filename para octetstream
// file.originalFilename http filename ou null se houver um erro de análise
// file.newFilename gerou hexoid ou o que options.filename retornou
// file.filepath nome do caminho padrão de acordo com options.uploadDir e options.filename
// file.filepath = CUSTOM_PATH // para alterar o caminho final
});
```
#### `'file'`
Emitido sempre que um par campo/arquivo é recebido. `file` é uma instância de
`File`.
```js
form.on('file', (formname, file) => {
// o mesmo que fileBegin, exceto
// é muito tarde para alterar file.filepath
// file.hash está disponível se options.hash foi usado
});
```
#### `'error'`
Emitido quando há um erro no processamento do formulário recebido. Uma solicitação que
apresenta um erro é pausada automaticamente, você terá que chamar manualmente
`request.resume()` se você quiser que a requisição continue disparando eventos `'data'`.
Pode ter `error.httpCode` e `error.code` anexados.
```js
form.on('error', (err) => {});
```
#### `'aborted'`
Emitido quando a requisição foi abortada pelo usuário. Agora isso pode ser devido a um
evento 'timeout' ou 'close' no soquete. Após este evento ser emitido, um
O evento `error` seguirá. No futuro, haverá um 'timeout' separado
evento (precisa de uma mudança no núcleo do nó).
```js
form.on('aborted', () => {});
```
#### `'end'`
Emitido quando toda a solicitação foi recebida e todos os arquivos contidos foram
liberados para o disco. Este é um ótimo lugar para você enviar sua resposta.
```js
form.on('end', () => {});
```
### Helpers
#### firstValues
Obtém os primeiros valores dos campos, como pré 3.0.0 sem passar múltiplos em uma
lista de exceções opcionais onde arrays de strings ainda são desejados (`` por exemplo)
```js
import { firstValues } from 'formidable/src/helpers/firstValues.js';
// ...
form.parse(request, async (error, fieldsMultiple, files) => {
if (error) {
//...
}
const exceptions = ['thisshouldbeanarray'];
const fieldsSingle = firstValues(form, fieldsMultiple, exceptions);
// ...
```
#### readBooleans
Html form input type="checkbox" envia apenas o valor "on" se marcado,
converta-o em booleanos para cada entrada que deve ser enviada como uma caixa de seleção, use somente após a chamada de firstValues ou similar.
```js
import { firstValues } from 'formidable/src/helpers/firstValues.js';
import { readBooleans } from 'formidable/src/helpers/readBooleans.js';
// ...
form.parse(request, async (error, fieldsMultiple, files) => {
if (error) {
//...
}
const fieldsSingle = firstValues(form, fieldsMultiple);
const expectedBooleans = ['checkbox1', 'wantsNewsLetter', 'hasACar'];
const fieldsWithBooleans = readBooleans(fieldsSingle, expectedBooleans);
// ...
```
## Changelog
[./CHANGELOG.md](./CHANGELOG.md)
## Ports & Créditos
- [multipart-parser](http://github.com/FooBarWidget/multipart-parser): um analisador C++ baseado em formidável
- [Ryan Dahl](https://x.com/rough__sea) por seu trabalho em
[http-parser](http://github.com/ry/http-parser) que inspirou fortemente o `multipart_parser.js` inicial.
## Contribuindo
Se a documentação não estiver clara ou tiver um erro de digitação, clique no botão `Edit` da página (ícone de lápis) e sugira uma correção.
Se você gostaria de nos ajudar a corrigir
um bug ou adicionar um novo recurso, verifique nosso [Contributing
Guide][contribuindo-url]. Pull requests são bem-vindos!
Agradecimentos vão para essas pessoas maravilhosas
([emoji key](https://allcontributors.org/docs/en/emoji-key)):
De uma [postagem do blog Felix](https://felixge.de/2013/03/11/the-pull-request-hack/):
- [Sven Lito](https://github.com/svnlto) por corrigir bugs e mesclar patches
- [egirshov](https://github.com/egirshov) por contribuir com muitas melhorias para o analisador multipartes formidável de nós
- [Andrew Kelley](https://github.com/superjoe30) por também ajudar a corrigir bugs e fazer melhorias
- [Mike Frey](https://github.com/mikefrey) por contribuir com suporte JSON
## Licença
Formidable é licenciado sob a [MIT License][license-url].
[codestyle-url]: https://github.com/airbnb/javascript
[codestyle-img]: https://badgen.net/badge/code%20style/airbnb%20%2B%20prettier/ff5a5f?icon=airbnb&cache=300
[codecov-url]: https://codecov.io/gh/node-formidable/formidable
[codecov-img]: https://badgen.net/codecov/c/github/node-formidable/formidable/master?icon=codecov
[npmv-canary-img]: https://badgen.net/npm/v/formidable/canary?icon=npm
[npmv-dev-img]: https://badgen.net/npm/v/formidable/dev?icon=npm
[npmv-img]: https://badgen.net/npm/v/formidable?icon=npm
[npmv-url]: https://npmjs.com/package/formidable
[license-img]: https://badgen.net/npm/license/formidable
[license-url]: https://github.com/node-formidable/formidable/blob/master/LICENSE
[chat-img]: https://badgen.net/badge/chat/on%20gitter/46BC99?icon=gitter
[chat-url]: https://gitter.im/node-formidable/Lobby
[libera-manifesto-url]: https://liberamanifesto.com
[libera-manifesto-img]: https://badgen.net/badge/libera/manifesto/grey
[renovateapp-url]: https://renovatebot.com
[renovateapp-img]: https://badgen.net/badge/renovate/enabled/green?cache=300
[prs-welcome-img]: https://badgen.net/badge/PRs/welcome/green?cache=300
[prs-welcome-url]: http://makeapullrequest.com
[twitter-url]: https://twitter.com/3a1fcBx0
[twitter-img]: https://badgen.net/twitter/follow/3a1fcBx0?icon=twitter&color=1da1f2&cache=300
[npm-weekly-img]: https://badgen.net/npm/dw/formidable?icon=npm&cache=300
[npm-monthly-img]: https://badgen.net/npm/dm/formidable?icon=npm&cache=300
[npm-yearly-img]: https://badgen.net/npm/dy/formidable?icon=npm&cache=300
[npm-alltime-img]: https://badgen.net/npm/dt/formidable?icon=npm&cache=300&label=total%20downloads
[nodejs-img]: https://badgen.net/badge/node/>=%2010.13/green?cache=300
[ccommits-url]: https://conventionalcommits.org/
[ccommits-img]: https://badgen.net/badge/conventional%20commits/v1.0.0/green?cache=300
[contributing-url]: https://github.com/node-formidable/.github/blob/master/CONTRIBUTING.md
[code_of_conduct-url]: https://github.com/node-formidable/.github/blob/master/CODE_OF_CONDUCT.md
[open-issue-url]: https://github.com/node-formidable/formidable/issues/new
[tidelift-url]: https://tidelift.com/subscription/pkg/npm-formidable?utm_source=npm-formidable&utm_medium=referral&utm_campaign=enterprise
[tidelift-img]: https://badgen.net/badge/tidelift/subscription/4B5168?labelColor=F6914D
[kofi-url]: https://ko-fi.com/tunnckoCore/commissions
[kofi-img]: https://badgen.net/badge/ko-fi/support/29abe0c2?cache=300&icon=https://rawcdn.githack.com/tunnckoCore/badgen-icons/f8264c6414e0bec449dd86f2241d50a9b89a1203/icons/kofi.svg
[linux-build-img]: https://badgen.net/github/checks/node-formidable/formidable/master?cache=30&label=linux%20build&icon=github
[macos-build-img]: https://badgen.net/github/checks/node-formidable/formidable/master?cache=30&label=macos%20build&icon=github
[build-url]: https://github.com/node-formidable/formidable/actions
================================================
FILE: VERSION_NOTES.md
================================================
# Important Notes for v1, v2, and v3
For more info, check the [CHANGELOG](https://github.com/node-formidable/formidable/blob/master/CHANGELOG.md) on the master branch.
## v1 is deprecated
All `v1` versions are deprecated in NPM for over 2 years. You can find it at `formidable@v1` on NPM, and on [v1 branch][v1branch] on GitHub.
We highly recommend to use `v2` or `v3`. Both are already in use by many, especially `v2` which was on `formidable@canary` for 2 years.
- **Status: Not Maintained!**
- We won't provide support or accept reports on that version.
- **No Backporting:** bugfixes, security fixes, or new features WILL NOT happen!
- Please move to at least **v2**!
- Try with installing `formidable@v2` and if still have the problem - report!
## v2 is going to be deprecated
The `v2` is available as `formidable@v2`.
The source code is available **only** on [v2 branch][v2branch].
If you want to use v2, it's recommended to lock and use the v2 dist-tag `formidable@v2-latest`.
**Main Differences from v1:**
- Better organization and modernized code, requiring newer Node.js versions (>= v10).
- A lot of bugfixes, closed issues, merged or closed PRs.
- Better docs, new features (plugins, parsers, options) and optimizations.
## v3 - ESModules, Monorepo structure
We recommend to use `formidable@latest` to install, as it uses more modern Node.js Streams, has support for more stuff.
You can see more info and track some ideas on [issue#635](https://github.com/node-formidable/formidable/issues/635).
- The source code can be found on the [master branch][v3branch] on GitHub.
- It is published as `formidable@latest`
- Dropping older Node.js versions, requiring higher than v12-v14.
- Dropping v1 compatibility.
- Rewritten to ESModules, more optimizations.
- Moving to monorepo structure, more plugins & helper utils.
[v1branch]: https://github.com/node-formidable/formidable/tree/v1-legacy
[v2branch]: https://github.com/node-formidable/formidable/tree/v2-latest
[v3branch]: https://github.com/node-formidable/formidable/tree/master
================================================
FILE: benchmark/2022-11-30-i5-9600k.txt
================================================
npm run bench
> formidable@3.2.5 bench
> node benchmark
4132.23 mb/sec
PS C:\files\formidable> node -v
v18.0.0
npm run bench
> formidable@3.2.5 bench
> node benchmark
3952.57 mb/sec
PS C:\files\formidable> node -v
v19.2.0
C:\files\formidable> bombardier --body-file="./README.md" --method=POST
--duration=10s --connections=100 http://localhost:3000/api/upload
Bombarding http://localhost:3000/api/upload for 10s using 100 connection(s)[====================================================================] 10s
Done!
Statistics Avg Stdev Max
Reqs/sec 2824.09 1512.74 6881.85
Latency 35.51ms 37.38ms 0.98s
HTTP codes:
1xx - 0, 2xx - 28163, 3xx - 0, 4xx - 0, 5xx - 0
others - 0
Throughput: 102.08MB/s
================================================
FILE: benchmark/e2e.txt
================================================
node ./benchmark/server.js
bombardier --body-file="./README.md" --method=POST --duration=10s --connections=100 http://localhost:3000/api/upload
================================================
FILE: benchmark/index.js
================================================
import assert from "node:assert";
import MultipartParser from '../src/parsers/Multipart.js';
const parser = new MultipartParser();
const customBoundary = '-----------------------------168072824752491622650073';
const mb = 1000; // 1GB
const buf = createMultipartBuffer(customBoundary, mb * 1024 * 1024);
const calls = {
partBegin: 0,
headerField: 0,
headerValue: 0,
headerEnd: 0,
headersEnd: 0,
partData: 0,
partEnd: 0,
end: 0,
};
const start = performance.now();
parser.initWithBoundary(customBoundary);
parser.on('data', ({ name }) => {
calls[name] += 1;
});
parser.write(buf);
const duration = performance.now() - start;
const mbPerSec = (mb / (duration / 1000)).toFixed(2);
console.log(`${mbPerSec} mb/sec`);
function createMultipartBuffer(boundary, size) {
const head =
`--${boundary}\r\n` +
`content-disposition: form-data; name="field1"\r\n` +
`\r\n`;
const tail = `\r\n--${boundary}--\r\n`;
const buffer = Buffer.alloc(size);
buffer.write(head, 0, 'ascii');
buffer.write(tail, buffer.length - tail.length, 'ascii');
return buffer;
}
process.on('exit', () => {
assert.deepStrictEqual(calls, {
partBegin: 1,
headerField: 1,
headerValue: 1,
headerEnd: 1,
headersEnd: 1,
partData: 1,
partEnd: 1,
end: 1,
});
// Object.keys(events).forEach((k) => {
// console.log(k, events[k]);
// // assert.equal(callbacks[k], 0, `${k} count off by ${callbacks[k]}`);
// });
});
================================================
FILE: benchmark/server.js
================================================
// inital copy of with-http.js
// made a copy so that examples can be changed without impacting tests
import http from 'node:http';
import slugify from '@sindresorhus/slugify';
import formidable, {errors as formidableErrors} from '../src/index.js';
const server = http.createServer((req, res) => {
// handle common internet errors
// to avoid server crash
req.on('error', console.error);
res.on('error', console.error);
if (req.url === '/api/upload' && req.method.toLowerCase() === 'post') {
const form = formidable({
uploadDir: `benchmark/testuploads`,
keepExtensions: true,
});
form.parse(req, (err, fields, files) => {
if (err) {
console.error(err);
res.writeHead(err.httpCode || 400, { 'Content-Type': 'text/plain' });
res.end(String(err));
return;
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ fields, files }, null, 2));
});
return;
}
// else not used in tests
});
server.listen(3000, () => {
console.log('Server listening on http://localhost:3000 ...');
});
================================================
FILE: examples/express-middleware.js
================================================
import express from 'express';
import formidable from '../src/index.js';
const app = express();
// middlewares that populates req.fields and req.body
const formMiddleWare = (req, res, next) => {
const form = formidable({});
form.parse(req, (err, fields, files) => {
if (err) {
next(err);
return;
}
req.fields = fields;
req.files = files;
next();
});
};
app.get('/', (req, res) => {
res.send(`
With "express" npm package
`);
});
// use middleware
app.post('/api/upload', formMiddleWare, (req, res, next) => {
res.json({
fields: req.fields,
files: req.files,
});
});
app.listen(3000, () => {
console.log('Server listening on http://localhost:3000 ...');
});
================================================
FILE: examples/forceBuffer.js
================================================
// warning: forcing file into a Buffer elminates the benefits of using streams and may cause memory overflow
import http from 'node:http';
import { Buffer } from 'node:buffer'
import { Writable } from 'node:stream';
import formidable from '../src/index.js';
const server = http.createServer((req, res) => {
if (req.url === '/api/upload' && req.method.toLowerCase() === 'post') {
// parse a file upload
const endBuffers = {};
const form = formidable({
fileWriteStreamHandler: (file) => {
const chunks = [];
const writable = new Writable({
write (chunk, enc, next) {
chunks.push(chunk);
next();
},
destroy() {
endBuffers = {};
},
final(cb) {
const buffer = Buffer.concat(chunks);
// if filename option is not provided file.newFilename will be a random string
endBuffers[file.newFilename] = buffer;
cb();
},
})
return writable;
},
});
form.parse(req, (err, fields, files) => {
// available here endBuffers
if (err) {
console.error(err);
res.writeHead(err.httpCode || 400, { 'Content-Type': 'text/plain' });
res.end(String(err));
return;
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ fields, files }, null, 2));
Object.entries(endBuffers).map(([key, value]) => {
console.log(key);
console.log(value.toString("utf8"));
});
});
return;
}
// show a file upload form
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(`
With Node.js "http" module
`);
});
server.listen(3000, () => {
console.log('Server listening on http://localhost:3000 ...');
});
================================================
FILE: examples/json.js
================================================
import http from 'node:http';
import util from 'node:util';
import formidable from '../src/index.js';
const PORT = 3000;
const server = http.createServer((req, res) => {
if (req.method !== 'POST') {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(`Please POST a JSON payload to http://localhost:${PORT}/`);
return;
}
const form = formidable();
const fields = {};
form
.on('error', (err) => {
console.error(err);
res.writeHead(500, { 'Content-Type': 'text/plain' });
res.end(`error:\n\n${util.inspect(err)}`);
})
.on('field', (field, value) => {
console.log(field, value);
fields[field] = value;
})
.on('end', () => {
console.log('-> post done from "end" event');
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(`received fields:\n\n${util.inspect(fields)}`);
});
form.parse(req);
});
server.listen(PORT, () => {
const chosenPort = server.address().port;
console.log(`Listening on http://localhost:${chosenPort}/`);
const body = JSON.stringify({
numbers: [1, 2, 3, 4, 5],
nested: { key: 'some val' },
});
const request = http.request(
{
host: 'localhost',
path: '/',
port: chosenPort,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'content-length': body.length,
},
},
(response) => {
console.log('\nServer responded with:');
console.log('Status:', response.statusCode);
response.pipe(process.stdout);
response.on('end', () => {
console.log('\n');
process.exit();
});
// const data = '';
// response.on('data', function(chunk) {
// data += chunk.toString('utf8');
// });
// response.on('end', function() {
// console.log('Response Data:');
// console.log(data);
// process.exit();
// });
},
);
request.end(body);
});
================================================
FILE: examples/log-file-content-to-console.js
================================================
import http from 'node:http';
import { Writable } from 'node:stream';
import formidable from '../src/index.js';
const server = http.createServer((req, res) => {
if (req.url === '/api/upload' && req.method.toLowerCase() === 'post') {
// parse a file upload
const form = formidable({
fileWriteStreamHandler: (/* file */) => {
const writable = Writable();
// eslint-disable-next-line no-underscore-dangle
writable._write = (chunk, enc, next) => {
console.log(chunk.toString());
next();
};
return writable;
},
});
form.parse(req, () => {
res.writeHead(200);
res.end();
});
return;
}
// show a file upload form
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(`
With Node.js "http" module
`);
});
server.listen(3000, () => {
console.log('Server listening on http://localhost:3000 ...');
});
================================================
FILE: examples/multipart-parser.js
================================================
import { Blob } from 'node:buffer';
import { Readable } from 'node:stream';
import { FormData, formDataToBlob } from 'formdata-polyfill/esm.min.js'
import { MultipartParser } from '../src/index.js';
const blob1 = new Blob(
['Content of a.txt.'],
{ type: 'text/plain' }
);
const blob2 = new Blob(
['Content of a.html. '],
{ type: 'text/html' }
);
const fd = new FormData();
fd.set('text', 'some text ...');
fd.set('z', 'text inside z');
fd.set('file1', blob1, 'a.txt');
fd.set('file2', blob2, 'a.html');
const multipartParser = new MultipartParser();
multipartParser.on('data', ({ name, buffer, start, end }) => {
console.log(`${name}:`);
if (buffer && start && end) {
console.log(String(buffer.slice(start, end)));
}
console.log();
});
multipartParser.on('error', console.error);
const blob = formDataToBlob(fd);
const boundary = blob.type.split('boundary=')[1];
multipartParser.initWithBoundary(boundary);
Readable.from(blob.stream()).pipe(multipartParser);
================================================
FILE: examples/multiples.js
================================================
import http from 'node:http';
import os from 'node:os';
import formidable from '../src/index.js';
const server = http.createServer((req, res) => {
if (req.url === '/') {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(`
`);
} else if (req.url === '/upload') {
const form = formidable({ uploadDir: os.tmpdir() });
form.parse(req, (err, fields, files) => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ err, fields, files }, null, 2));
});
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('404');
}
});
server.listen(3000, () => {
console.log('Server listening on http://localhost:3000 ...');
});
================================================
FILE: examples/store-files-on-s3.js
================================================
// To test this example you have to install aws-sdk nodejs package and create a bucket named "demo-bucket"
import http from 'node:http';
import { PassThrough } from 'node:stream';
import AWS from 'aws-sdk';
import formidable from '../src/index.js';
const s3Client = new AWS.S3({
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_KEY,
},
});
const uploadStream = (file) => {
const pass = new PassThrough();
s3Client.upload(
{
Bucket: 'demo-bucket',
Key: file.newFilename,
Body: pass,
},
(err, data) => {
console.log(err, data);
},
);
return pass;
};
const server = http.createServer((req, res) => {
if (req.url === '/api/upload' && req.method.toLowerCase() === 'post') {
// parse a file upload
const form = formidable({
fileWriteStreamHandler: uploadStream,
});
form.parse(req, () => {
res.writeHead(200);
res.end();
});
return;
}
// show a file upload form
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(`
With Node.js "http" module
`);
});
server.listen(3000, () => {
console.log('Server listening on http://localhost:3000 ...');
});
================================================
FILE: examples/upload-multiple-files.js
================================================
import http from 'node:http';
import util from 'node:util';
import os from 'node:os';
import formidable from '../src/index.js';
const server = http.createServer((req, res) => {
if (req.url === '/') {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(`
`);
} else if (req.url === '/upload') {
const form = formidable({ uploadDir: os.tmpdir() });
const files = [];
const fields = [];
form
.on('field', (fieldName, value) => {
console.log(fieldName, value);
fields.push({ fieldName, value });
})
.on('file', (fieldName, file) => {
console.log(fieldName, file);
files.push({ fieldName, file });
})
.on('end', () => {
console.log('-> upload done');
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write(`received fields:\n\n${util.inspect(fields)}`);
res.write('\n\n');
res.end(`received files:\n\n${util.inspect(files)}`);
});
form.parse(req);
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('404');
}
});
server.listen(3000, () => {
console.log('Server listening on http://localhost:3000 ...');
});
================================================
FILE: examples/urlencoded-no-enctype.js
================================================
import http from 'node:http';
import util from 'node:util';
import formidable from '../src/index.js';
const server = http.createServer((req, res) => {
if (req.url === '/') {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(`
`);
} else if (req.url === '/post') {
const form = formidable();
const fields = [];
form
.on('error', (err) => {
console.log('err!', err);
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(`error:\n\n${util.inspect(err)}`);
})
.on('field', (fieldName, fieldValue) => {
console.log('fieldName:', fieldName);
console.log('fieldValue:', fieldValue);
fields.push({ fieldName, fieldValue });
})
.on('end', () => {
console.log('-> post done from "end" event');
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(`received fields:\n\n${util.inspect(fields)}`);
});
form.parse(req, () => {
console.log('-> post done from callback');
// res.writeHead(200, { 'Content-Type': 'text/plain' });
// res.end(`received fields:\n\n${util.inspect(fields)}`);
});
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('404');
}
});
server.listen(3000, () => {
console.log('Server listening on http://localhost:3000 ...');
});
================================================
FILE: examples/with-express.js
================================================
import express from 'express';
import formidable from '../src/index.js';
const app = express();
app.get('/', (req, res) => {
res.send(`
With "express" npm package
`);
});
app.post('/api/upload', (req, res, next) => {
const form = formidable({ });
form.parse(req, (err, fields, files) => {
if (err) {
next(err);
return;
}
res.json({ fields, files });
});
});
app.listen(3000, () => {
console.log('Server listening on http://localhost:3000 ...');
});
================================================
FILE: examples/with-http.js
================================================
import http from 'node:http';
import slugify from '@sindresorhus/slugify';
import formidable, {errors as formidableErrors} from '../src/index.js';
const server = http.createServer((req, res) => {
// handle common internet errors
// to avoid server crash
req.on('error', console.error);
res.on('error', console.error);
if (req.url === '/api/upload' && req.method.toLowerCase() === 'post') {
// parse a file upload
const form = formidable({
defaultInvalidName: 'invalid',
uploadDir: `uploads`,
keepExtensions: true,
createDirsFromUploads: true,
allowEmptyFiles: true,
minFileSize: 0,
filename(name, ext, part, form) {
/* name basename of the http originalFilename
ext with the dot ".txt" only if keepExtensions is true
*/
// originalFilename will have slashes with relative path if a
// directory was uploaded
const {originalFilename} = part;
if (!originalFilename) {
return 'invalid';
}
// return 'yo.txt'; // or completly different name
// return 'z/yo.txt'; // subdirectory
return originalFilename.split("/").map((subdir) => {
return slugify(subdir, {separator: ''}); // slugify to avoid invalid filenames
}).join("/").substr(0, 100); // substr to define a maximum
},
filter: function ({name, originalFilename, mimetype}) {
return Boolean(originalFilename);
// keep only images
// return mimetype?.includes("image");
}
// maxTotalFileSize: 4000,
// maxFileSize: 1000,
});
form.parse(req, (err, fields, files) => {
if (err) {
console.error(err);
res.writeHead(err.httpCode || 400, { 'Content-Type': 'text/plain' });
res.end(String(err));
return;
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ fields, files }, null, 2));
});
return;
}
// else show a file upload form
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(`
With Node.js "http" module
`);
});
server.listen(3000, () => {
console.log('Server listening on http://localhost:3000 ...');
});
================================================
FILE: examples/with-koa2.js
================================================
import Koa from 'koa';
import formidable from '../src/index.js';
const app = new Koa();
app.on('error', (err) => {
console.error('server error', err);
});
app.use(async (ctx, next) => {
if (ctx.url === '/api/upload' && ctx.method.toLowerCase() === 'post') {
let i = 0;
const form = formidable({
keepExtensions: true,
// must return absolute path
filename: (part, $self) => {
i += 1;
return `${$self.uploadDir}/sasasa${i}`;
},
});
// not very elegant, but that's for now if you don't want touse `koa-better-body`
// or other middlewares.
await new Promise((resolve, reject) => {
form.parse(ctx.req, (err, fields, files) => {
if (err) {
reject(err);
return;
}
ctx.set('Content-Type', 'application/json');
ctx.status = 200;
ctx.state = { fields, files };
ctx.body = JSON.stringify(ctx.state, null, 2);
resolve();
});
});
await next();
return;
}
// show a file upload form
ctx.set('Content-Type', 'text/html');
ctx.status = 200;
ctx.body = `
With "koa" npm package
`;
});
app.use((ctx) => {
console.log('The next middleware is called');
console.log('Results:', ctx.state);
});
app.listen(3000, () => {
console.log('Server listening on http://localhost:3000 ...');
});
================================================
FILE: nyc.config.js
================================================
'use strict';
module.exports = {
statements: 70,
branches: 70,
functions: 70,
lines: 70,
'check-coverage': true,
exclude: ['test'],
include: ['src'],
reporter: ['text', 'text-summary', 'lcov', 'clover'],
};
================================================
FILE: package.json
================================================
{
"name": "formidable",
"version": "3.5.4",
"license": "MIT",
"description": "A node.js module for parsing form data, especially file uploads.",
"homepage": "https://github.com/node-formidable/formidable",
"funding": "https://ko-fi.com/tunnckoCore/commissions",
"repository": "node-formidable/formidable",
"type": "module",
"main": "./dist/index.cjs",
"exports": {
".": {
"import": {
"default": "./src/index.js"
},
"require": {
"default": "./dist/index.cjs"
},
"default": "./dist/index.cjs"
},
"./src/helpers/*.js": {
"import": {
"default": "./src/helpers/*.js"
},
"require": {
"default": "./dist/helpers/*.cjs"
}
},
"./src/parsers/*.js": {
"import": {
"default": "./src/parsers/*.js"
},
"require": {
"default": "./dist/index.cjs"
}
}
},
"files": [
"src",
"./dist",
"./CHANGELOG",
"./README.md",
"./README_pt_BR.md"
],
"publishConfig": {
"access": "public",
"tag": "latest"
},
"scripts": {
"build-package": "rollup --config ./tool/rollup.config.js",
"prepublishOnly": "pnpm run build-package",
"bench": "node benchmark",
"bench2prep": "node benchmark/server.js",
"bench2": "bombardier --body-file=\"./README.md\" --method=POST --duration=10s --connections=100 http://localhost:3000/api/upload",
"fmt": "pnpm run fmt:prepare '**/*'",
"fmt:prepare": "prettier --write",
"lint": "pnpm run lint:prepare .",
"lint:prepare": "eslint --cache --fix --quiet --format codeframe",
"fresh": "rm -rf ./node_modules",
"test-specific": "node --disable-warning=ExperimentalWarning --experimental-vm-modules ./node_modules/jest/bin/jest.js --testPathPattern=test/standalone/keep-alive-error.test.js",
"test-jest": "node --disable-warning=ExperimentalWarning --experimental-vm-modules ./node_modules/jest/bin/jest.js --testPathPattern=test/ --coverage",
"test-node": "node --disable-warning=ExperimentalWarning --test ./test-node/**/*.test.js",
"test-jest:ci": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --testPathPattern=test/ --coverage",
"test:local": "pnpm run test-node && pnpm run test-jest",
"audit": "pnpm audit --prod --fix",
"pretest": "rm -rf ./test/tmp && mkdir ./test/tmp",
"test": "pnpm run audit && node --test ./test-node/**/*.test.js && pnpm run test-jest:ci"
},
"dependencies": {
"@paralleldrive/cuid2": "2.2.2",
"dezalgo": "^1.0.4",
"once": "^1.4.0"
},
"packageManager": "pnpm@10.8.1",
"devDependencies": {
"@rollup/plugin-commonjs": "^25.0.2",
"@rollup/plugin-node-resolve": "^15.1.0",
"@sindresorhus/slugify": "^2.1.0",
"@tunnckocore/prettier-config": "1.3.8",
"eslint": "6.8.0",
"eslint-config-airbnb-base": "14.1.0",
"eslint-config-prettier": "6.11.0",
"eslint-plugin-import": "2.20.2",
"eslint-plugin-prettier": "3.1.3",
"express": "^4.21.1",
"formdata-polyfill": "^4.0.10",
"jest": "27.2.4",
"koa": "2.16.1",
"nyc": "15.1.0",
"prettier": "2.0.5",
"prettier-plugin-pkgjson": "0.2.8",
"rollup": "^3.25.3",
"supertest": "6.1.6"
},
"engines": {
"node": ">=14.0.0"
},
"jest": {
"verbose": true
},
"keywords": [
"multipart",
"form",
"data",
"querystring",
"www",
"json",
"ulpoad",
"file"
]
}
================================================
FILE: pnpm-lock.yaml
================================================
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
'@paralleldrive/cuid2':
specifier: 2.2.2
version: 2.2.2
dezalgo:
specifier: ^1.0.4
version: 1.0.4
once:
specifier: ^1.4.0
version: 1.4.0
devDependencies:
'@rollup/plugin-commonjs':
specifier: ^25.0.2
version: 25.0.8(rollup@3.29.5)
'@rollup/plugin-node-resolve':
specifier: ^15.1.0
version: 15.3.1(rollup@3.29.5)
'@sindresorhus/slugify':
specifier: ^2.1.0
version: 2.2.1
'@tunnckocore/prettier-config':
specifier: 1.3.8
version: 1.3.8(prettier-plugin-pkgjson@0.2.8(prettier@2.0.5))(prettier@2.0.5)
eslint:
specifier: 6.8.0
version: 6.8.0
eslint-config-airbnb-base:
specifier: 14.1.0
version: 14.1.0(eslint-plugin-import@2.20.2(eslint@6.8.0))(eslint@6.8.0)
eslint-config-prettier:
specifier: 6.11.0
version: 6.11.0(eslint@6.8.0)
eslint-plugin-import:
specifier: 2.20.2
version: 2.20.2(eslint@6.8.0)
eslint-plugin-prettier:
specifier: 3.1.3
version: 3.1.3(eslint@6.8.0)(prettier@2.0.5)
express:
specifier: ^4.21.1
version: 4.21.2
formdata-polyfill:
specifier: ^4.0.10
version: 4.0.10
jest:
specifier: 27.2.4
version: 27.2.4
koa:
specifier: 2.16.1
version: 2.16.1
nyc:
specifier: 15.1.0
version: 15.1.0
prettier:
specifier: 2.0.5
version: 2.0.5
prettier-plugin-pkgjson:
specifier: 0.2.8
version: 0.2.8(prettier@2.0.5)
rollup:
specifier: ^3.25.3
version: 3.29.5
supertest:
specifier: 6.1.6
version: 6.1.6
packages:
'@ampproject/remapping@2.3.0':
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
engines: {node: '>=6.0.0'}
'@babel/code-frame@7.26.2':
resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==}
engines: {node: '>=6.9.0'}
'@babel/compat-data@7.26.8':
resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==}
engines: {node: '>=6.9.0'}
'@babel/core@7.26.10':
resolution: {integrity: sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==}
engines: {node: '>=6.9.0'}
'@babel/generator@7.27.0':
resolution: {integrity: sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==}
engines: {node: '>=6.9.0'}
'@babel/helper-compilation-targets@7.27.0':
resolution: {integrity: sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==}
engines: {node: '>=6.9.0'}
'@babel/helper-module-imports@7.25.9':
resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==}
engines: {node: '>=6.9.0'}
'@babel/helper-module-transforms@7.26.0':
resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
'@babel/helper-plugin-utils@7.26.5':
resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==}
engines: {node: '>=6.9.0'}
'@babel/helper-string-parser@7.25.9':
resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==}
engines: {node: '>=6.9.0'}
'@babel/helper-validator-identifier@7.25.9':
resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==}
engines: {node: '>=6.9.0'}
'@babel/helper-validator-option@7.25.9':
resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==}
engines: {node: '>=6.9.0'}
'@babel/helpers@7.27.0':
resolution: {integrity: sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==}
engines: {node: '>=6.9.0'}
'@babel/parser@7.27.0':
resolution: {integrity: sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==}
engines: {node: '>=6.0.0'}
hasBin: true
'@babel/plugin-syntax-async-generators@7.8.4':
resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/plugin-syntax-bigint@7.8.3':
resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/plugin-syntax-class-properties@7.12.13':
resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/plugin-syntax-class-static-block@7.14.5':
resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/plugin-syntax-import-attributes@7.26.0':
resolution: {integrity: sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/plugin-syntax-import-meta@7.10.4':
resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/plugin-syntax-json-strings@7.8.3':
resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/plugin-syntax-logical-assignment-operators@7.10.4':
resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/plugin-syntax-nullish-coalescing-operator@7.8.3':
resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/plugin-syntax-numeric-separator@7.10.4':
resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/plugin-syntax-object-rest-spread@7.8.3':
resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/plugin-syntax-optional-catch-binding@7.8.3':
resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/plugin-syntax-optional-chaining@7.8.3':
resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/plugin-syntax-private-property-in-object@7.14.5':
resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/plugin-syntax-top-level-await@7.14.5':
resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/plugin-syntax-typescript@7.25.9':
resolution: {integrity: sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/template@7.27.0':
resolution: {integrity: sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==}
engines: {node: '>=6.9.0'}
'@babel/traverse@7.27.0':
resolution: {integrity: sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==}
engines: {node: '>=6.9.0'}
'@babel/types@7.27.0':
resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==}
engines: {node: '>=6.9.0'}
'@bcoe/v8-coverage@0.2.3':
resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
'@istanbuljs/load-nyc-config@1.1.0':
resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
engines: {node: '>=8'}
'@istanbuljs/schema@0.1.3':
resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==}
engines: {node: '>=8'}
'@jest/console@27.5.1':
resolution: {integrity: sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
'@jest/core@27.5.1':
resolution: {integrity: sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
peerDependencies:
node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
peerDependenciesMeta:
node-notifier:
optional: true
'@jest/environment@27.5.1':
resolution: {integrity: sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
'@jest/fake-timers@27.5.1':
resolution: {integrity: sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
'@jest/globals@27.5.1':
resolution: {integrity: sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
'@jest/reporters@27.5.1':
resolution: {integrity: sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
peerDependencies:
node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
peerDependenciesMeta:
node-notifier:
optional: true
'@jest/source-map@27.5.1':
resolution: {integrity: sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
'@jest/test-result@27.5.1':
resolution: {integrity: sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
'@jest/test-sequencer@27.5.1':
resolution: {integrity: sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
'@jest/transform@27.5.1':
resolution: {integrity: sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
'@jest/types@27.5.1':
resolution: {integrity: sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
'@jridgewell/gen-mapping@0.3.8':
resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==}
engines: {node: '>=6.0.0'}
'@jridgewell/resolve-uri@3.1.2':
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
engines: {node: '>=6.0.0'}
'@jridgewell/set-array@1.2.1':
resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
engines: {node: '>=6.0.0'}
'@jridgewell/sourcemap-codec@1.5.0':
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
'@jridgewell/trace-mapping@0.3.25':
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
'@noble/hashes@1.8.0':
resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==}
engines: {node: ^14.21.3 || >=16}
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
'@nodelib/fs.stat@2.0.5':
resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
engines: {node: '>= 8'}
'@nodelib/fs.walk@1.2.8':
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
engines: {node: '>= 8'}
'@paralleldrive/cuid2@2.2.2':
resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==}
'@rollup/plugin-commonjs@25.0.8':
resolution: {integrity: sha512-ZEZWTK5n6Qde0to4vS9Mr5x/0UZoqCxPVR9KRUjU4kA2sO7GEUn1fop0DAwpO6z0Nw/kJON9bDmSxdWxO/TT1A==}
engines: {node: '>=14.0.0'}
peerDependencies:
rollup: ^2.68.0||^3.0.0||^4.0.0
peerDependenciesMeta:
rollup:
optional: true
'@rollup/plugin-node-resolve@15.3.1':
resolution: {integrity: sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==}
engines: {node: '>=14.0.0'}
peerDependencies:
rollup: ^2.78.0||^3.0.0||^4.0.0
peerDependenciesMeta:
rollup:
optional: true
'@rollup/pluginutils@5.1.4':
resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==}
engines: {node: '>=14.0.0'}
peerDependencies:
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
peerDependenciesMeta:
rollup:
optional: true
'@sindresorhus/slugify@2.2.1':
resolution: {integrity: sha512-MkngSCRZ8JdSOCHRaYd+D01XhvU3Hjy6MGl06zhOk614hp9EOAp5gIkBeQg7wtmxpitU6eAL4kdiRMcJa2dlrw==}
engines: {node: '>=12'}
'@sindresorhus/transliterate@1.6.0':
resolution: {integrity: sha512-doH1gimEu3A46VX6aVxpHTeHrytJAG6HgdxntYnCFiIFHEM/ZGpG8KiZGBChchjQmG0XFIBL552kBTjVcMZXwQ==}
engines: {node: '>=12'}
'@sinonjs/commons@1.8.6':
resolution: {integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==}
'@sinonjs/fake-timers@8.1.0':
resolution: {integrity: sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==}
'@tootallnate/once@1.1.2':
resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==}
engines: {node: '>= 6'}
'@tunnckocore/prettier-config@1.3.8':
resolution: {integrity: sha512-vhWfo1bgHMGR8qj8w9clxsanIDXYKTge/8kL23qVDfIMLOjam4SCyWgJi5ueOZ+drQJL7y3ni0y+o9lFvxaW+Q==}
engines: {node: '>=10.13'}
peerDependencies:
prettier: ^2.0.2
prettier-plugin-pkgjson: ^0.2.3
'@types/babel__core@7.20.5':
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
'@types/babel__generator@7.27.0':
resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==}
'@types/babel__template@7.4.4':
resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
'@types/babel__traverse@7.20.7':
resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==}
'@types/estree@1.0.7':
resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
'@types/glob@7.2.0':
resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==}
'@types/graceful-fs@4.1.9':
resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==}
'@types/istanbul-lib-coverage@2.0.6':
resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
'@types/istanbul-lib-report@3.0.3':
resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==}
'@types/istanbul-reports@3.0.4':
resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==}
'@types/minimatch@5.1.2':
resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==}
'@types/node@22.14.1':
resolution: {integrity: sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==}
'@types/prettier@2.7.3':
resolution: {integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==}
'@types/resolve@1.20.2':
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
'@types/stack-utils@2.0.3':
resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==}
'@types/yargs-parser@21.0.3':
resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==}
'@types/yargs@16.0.9':
resolution: {integrity: sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==}
abab@2.0.6:
resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
deprecated: Use your platform's native atob() and btoa() methods instead
accepts@1.3.8:
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
engines: {node: '>= 0.6'}
acorn-globals@6.0.0:
resolution: {integrity: sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==}
acorn-jsx@5.3.2:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
acorn-walk@7.2.0:
resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==}
engines: {node: '>=0.4.0'}
acorn@7.4.1:
resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==}
engines: {node: '>=0.4.0'}
hasBin: true
acorn@8.14.1:
resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==}
engines: {node: '>=0.4.0'}
hasBin: true
agent-base@6.0.2:
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
engines: {node: '>= 6.0.0'}
aggregate-error@3.1.0:
resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
engines: {node: '>=8'}
ajv@6.12.6:
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
ansi-escapes@4.3.2:
resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
engines: {node: '>=8'}
ansi-regex@4.1.1:
resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==}
engines: {node: '>=6'}
ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
ansi-styles@3.2.1:
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
engines: {node: '>=4'}
ansi-styles@4.3.0:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
ansi-styles@5.2.0:
resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
engines: {node: '>=10'}
anymatch@3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
append-transform@2.0.0:
resolution: {integrity: sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==}
engines: {node: '>=8'}
archy@1.0.0:
resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==}
argparse@1.0.10:
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
array-buffer-byte-length@1.0.2:
resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==}
engines: {node: '>= 0.4'}
array-flatten@1.1.1:
resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==}
array-includes@3.1.8:
resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==}
engines: {node: '>= 0.4'}
array-union@2.1.0:
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
engines: {node: '>=8'}
array.prototype.flat@1.3.3:
resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==}
engines: {node: '>= 0.4'}
arraybuffer.prototype.slice@1.0.4:
resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==}
engines: {node: '>= 0.4'}
asap@2.0.6:
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
astral-regex@1.0.0:
resolution: {integrity: sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==}
engines: {node: '>=4'}
async-function@1.0.0:
resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
engines: {node: '>= 0.4'}
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
available-typed-arrays@1.0.7:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'}
babel-jest@27.5.1:
resolution: {integrity: sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
peerDependencies:
'@babel/core': ^7.8.0
babel-plugin-istanbul@6.1.1:
resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==}
engines: {node: '>=8'}
babel-plugin-jest-hoist@27.5.1:
resolution: {integrity: sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
babel-preset-current-node-syntax@1.1.0:
resolution: {integrity: sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==}
peerDependencies:
'@babel/core': ^7.0.0
babel-preset-jest@27.5.1:
resolution: {integrity: sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
peerDependencies:
'@babel/core': ^7.0.0
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
body-parser@1.20.3:
resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
brace-expansion@1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
brace-expansion@2.0.1:
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
braces@3.0.3:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
browser-process-hrtime@1.0.0:
resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==}
browserslist@4.24.4:
resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
bser@2.1.1:
resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
buffer-from@1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
bytes@3.1.2:
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
engines: {node: '>= 0.8'}
cache-content-type@1.0.1:
resolution: {integrity: sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==}
engines: {node: '>= 6.0.0'}
caching-transform@4.0.0:
resolution: {integrity: sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==}
engines: {node: '>=8'}
call-bind-apply-helpers@1.0.2:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
call-bind@1.0.8:
resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==}
engines: {node: '>= 0.4'}
call-bound@1.0.4:
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
engines: {node: '>= 0.4'}
callsites@3.1.0:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
camelcase@5.3.1:
resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
engines: {node: '>=6'}
camelcase@6.3.0:
resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
engines: {node: '>=10'}
caniuse-lite@1.0.30001715:
resolution: {integrity: sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw==}
chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
char-regex@1.0.2:
resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
engines: {node: '>=10'}
chardet@0.7.0:
resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
ci-info@3.9.0:
resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==}
engines: {node: '>=8'}
cjs-module-lexer@1.4.3:
resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==}
clean-stack@2.2.0:
resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
engines: {node: '>=6'}
cli-cursor@3.1.0:
resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
engines: {node: '>=8'}
cli-width@3.0.0:
resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==}
engines: {node: '>= 10'}
cliui@6.0.0:
resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
cliui@7.0.4:
resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
co@4.6.0:
resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
collect-v8-coverage@1.0.2:
resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==}
color-convert@1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
color-name@1.1.3:
resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
commondir@1.0.1:
resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
component-emitter@1.3.1:
resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==}
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
confusing-browser-globals@1.0.11:
resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==}
contains-path@0.1.0:
resolution: {integrity: sha512-OKZnPGeMQy2RPaUIBPFFd71iNf4791H12MCRuVQDnzGRwCYNYmTDy5pdafo2SLAcEMKzTOQnLWG4QdcjeJUMEg==}
engines: {node: '>=0.10.0'}
content-disposition@0.5.4:
resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
engines: {node: '>= 0.6'}
content-type@1.0.5:
resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
engines: {node: '>= 0.6'}
convert-source-map@1.9.0:
resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==}
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
cookie-signature@1.0.6:
resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
cookie@0.7.1:
resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==}
engines: {node: '>= 0.6'}
cookiejar@2.1.4:
resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==}
cookies@0.9.1:
resolution: {integrity: sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==}
engines: {node: '>= 0.8'}
cross-spawn@6.0.6:
resolution: {integrity: sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==}
engines: {node: '>=4.8'}
cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
cssom@0.3.8:
resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==}
cssom@0.4.4:
resolution: {integrity: sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==}
cssstyle@2.3.0:
resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==}
engines: {node: '>=8'}
data-urls@2.0.0:
resolution: {integrity: sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==}
engines: {node: '>=10'}
data-view-buffer@1.0.2:
resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==}
engines: {node: '>= 0.4'}
data-view-byte-length@1.0.2:
resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==}
engines: {node: '>= 0.4'}
data-view-byte-offset@1.0.1:
resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==}
engines: {node: '>= 0.4'}
debug@2.6.9:
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
debug@3.2.7:
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
debug@4.4.0:
resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
decamelize@1.2.0:
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
engines: {node: '>=0.10.0'}
decimal.js@10.5.0:
resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==}
dedent@0.7.0:
resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==}
deep-equal@1.0.1:
resolution: {integrity: sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==}
deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
deepmerge@4.3.1:
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
engines: {node: '>=0.10.0'}
default-require-extensions@3.0.1:
resolution: {integrity: sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==}
engines: {node: '>=8'}
define-data-property@1.1.4:
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
engines: {node: '>= 0.4'}
define-properties@1.2.1:
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
engines: {node: '>= 0.4'}
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
delegates@1.0.0:
resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
depd@1.1.2:
resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==}
engines: {node: '>= 0.6'}
depd@2.0.0:
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
engines: {node: '>= 0.8'}
destroy@1.2.0:
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
detect-indent@6.1.0:
resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==}
engines: {node: '>=8'}
detect-newline@3.1.0:
resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
engines: {node: '>=8'}
dezalgo@1.0.4:
resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==}
diff-sequences@27.5.1:
resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
dir-glob@3.0.1:
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
engines: {node: '>=8'}
doctrine@1.5.0:
resolution: {integrity: sha512-lsGyRuYr4/PIB0txi+Fy2xOMI2dGaTguCaotzFGkVZuKR5usKfcRWIFKNM3QNrU7hh/+w2bwTW+ZeXPK5l8uVg==}
engines: {node: '>=0.10.0'}
doctrine@3.0.0:
resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
engines: {node: '>=6.0.0'}
domexception@2.0.1:
resolution: {integrity: sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==}
engines: {node: '>=8'}
deprecated: Use your platform's native DOMException instead
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
electron-to-chromium@1.5.140:
resolution: {integrity: sha512-o82Rj+ONp4Ip7Cl1r7lrqx/pXhbp/lh9DpKcMNscFJdh8ebyRofnc7Sh01B4jx403RI0oqTBvlZ7OBIZLMr2+Q==}
emittery@0.8.1:
resolution: {integrity: sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==}
engines: {node: '>=10'}
emoji-regex@7.0.3:
resolution: {integrity: sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==}
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
encodeurl@1.0.2:
resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
engines: {node: '>= 0.8'}
encodeurl@2.0.0:
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
engines: {node: '>= 0.8'}
error-ex@1.3.2:
resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
es-abstract@1.23.9:
resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==}
engines: {node: '>= 0.4'}
es-define-property@1.0.1:
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
engines: {node: '>= 0.4'}
es-errors@1.3.0:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
es-object-atoms@1.1.1:
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'}
es-set-tostringtag@2.1.0:
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
engines: {node: '>= 0.4'}
es-shim-unscopables@1.1.0:
resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==}
engines: {node: '>= 0.4'}
es-to-primitive@1.3.0:
resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==}
engines: {node: '>= 0.4'}
es6-error@4.1.1:
resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==}
escalade@3.2.0:
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
engines: {node: '>=6'}
escape-html@1.0.3:
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
escape-string-regexp@1.0.5:
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
engines: {node: '>=0.8.0'}
escape-string-regexp@2.0.0:
resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==}
engines: {node: '>=8'}
escape-string-regexp@5.0.0:
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
engines: {node: '>=12'}
escodegen@2.1.0:
resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==}
engines: {node: '>=6.0'}
hasBin: true
eslint-config-airbnb-base@14.1.0:
resolution: {integrity: sha512-+XCcfGyCnbzOnktDVhwsCAx+9DmrzEmuwxyHUJpw+kqBVT744OUBrB09khgFKlK1lshVww6qXGsYPZpavoNjJw==}
engines: {node: '>= 6'}
peerDependencies:
eslint: ^5.16.0 || ^6.8.0
eslint-plugin-import: ^2.20.1
eslint-config-prettier@6.11.0:
resolution: {integrity: sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA==}
hasBin: true
peerDependencies:
eslint: '>=3.14.1'
eslint-import-resolver-node@0.3.9:
resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
eslint-module-utils@2.12.0:
resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==}
engines: {node: '>=4'}
peerDependencies:
'@typescript-eslint/parser': '*'
eslint: '*'
eslint-import-resolver-node: '*'
eslint-import-resolver-typescript: '*'
eslint-import-resolver-webpack: '*'
peerDependenciesMeta:
'@typescript-eslint/parser':
optional: true
eslint:
optional: true
eslint-import-resolver-node:
optional: true
eslint-import-resolver-typescript:
optional: true
eslint-import-resolver-webpack:
optional: true
eslint-plugin-import@2.20.2:
resolution: {integrity: sha512-FObidqpXrR8OnCh4iNsxy+WACztJLXAHBO5hK79T1Hc77PgQZkyDGA5Ag9xAvRpglvLNxhH/zSmZ70/pZ31dHg==}
engines: {node: '>=4'}
peerDependencies:
'@typescript-eslint/parser': '*'
eslint: 2.x - 6.x
peerDependenciesMeta:
'@typescript-eslint/parser':
optional: true
eslint-plugin-prettier@3.1.3:
resolution: {integrity: sha512-+HG5jmu/dN3ZV3T6eCD7a4BlAySdN7mLIbJYo0z1cFQuI+r2DiTJEFeF68ots93PsnrMxbzIZ2S/ieX+mkrBeQ==}
engines: {node: '>=6.0.0'}
peerDependencies:
eslint: '>= 5.0.0'
prettier: '>= 1.13.0'
eslint-scope@5.1.1:
resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
engines: {node: '>=8.0.0'}
eslint-utils@1.4.3:
resolution: {integrity: sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==}
engines: {node: '>=6'}
eslint-visitor-keys@1.3.0:
resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==}
engines: {node: '>=4'}
eslint@6.8.0:
resolution: {integrity: sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==}
engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1}
deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options.
hasBin: true
espree@6.2.1:
resolution: {integrity: sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==}
engines: {node: '>=6.0.0'}
esprima@4.0.1:
resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
engines: {node: '>=4'}
hasBin: true
esquery@1.6.0:
resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
engines: {node: '>=0.10'}
esrecurse@4.3.0:
resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
engines: {node: '>=4.0'}
estraverse@4.3.0:
resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==}
engines: {node: '>=4.0'}
estraverse@5.3.0:
resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
engines: {node: '>=4.0'}
estree-walker@2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
esutils@2.0.3:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'}
etag@1.8.1:
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
engines: {node: '>= 0.6'}
execa@5.1.1:
resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
engines: {node: '>=10'}
exit@0.1.2:
resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==}
engines: {node: '>= 0.8.0'}
expect@27.5.1:
resolution: {integrity: sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
express@4.21.2:
resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==}
engines: {node: '>= 0.10.0'}
external-editor@3.1.0:
resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==}
engines: {node: '>=4'}
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
fast-diff@1.3.0:
resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
fast-glob@3.3.3:
resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
engines: {node: '>=8.6.0'}
fast-json-stable-stringify@2.1.0:
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
fast-levenshtein@2.0.6:
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
fast-safe-stringify@2.1.1:
resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
fastq@1.19.1:
resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
fb-watchman@2.0.2:
resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==}
fetch-blob@3.2.0:
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
engines: {node: ^12.20 || >= 14.13}
figures@3.2.0:
resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
engines: {node: '>=8'}
file-entry-cache@5.0.1:
resolution: {integrity: sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==}
engines: {node: '>=4'}
fill-range@7.1.1:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
finalhandler@1.3.1:
resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==}
engines: {node: '>= 0.8'}
find-cache-dir@3.3.2:
resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==}
engines: {node: '>=8'}
find-up@2.1.0:
resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==}
engines: {node: '>=4'}
find-up@4.1.0:
resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
engines: {node: '>=8'}
flat-cache@2.0.1:
resolution: {integrity: sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==}
engines: {node: '>=4'}
flatted@2.0.2:
resolution: {integrity: sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==}
for-each@0.3.5:
resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
engines: {node: '>= 0.4'}
foreground-child@2.0.0:
resolution: {integrity: sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==}
engines: {node: '>=8.0.0'}
form-data@3.0.3:
resolution: {integrity: sha512-q5YBMeWy6E2Un0nMGWMgI65MAKtaylxfNJGJxpGh45YDciZB4epbWpaAfImil6CPAPTYB4sh0URQNDRIZG5F2w==}
engines: {node: '>= 6'}
formdata-polyfill@4.0.10:
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
engines: {node: '>=12.20.0'}
formidable@1.2.6:
resolution: {integrity: sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==}
deprecated: 'Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau'
forwarded@0.2.0:
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
engines: {node: '>= 0.6'}
fresh@0.5.2:
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
engines: {node: '>= 0.6'}
fromentries@1.3.2:
resolution: {integrity: sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==}
fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
function.prototype.name@1.1.8:
resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==}
engines: {node: '>= 0.4'}
functional-red-black-tree@1.0.1:
resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==}
functions-have-names@1.2.3:
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
gensync@1.0.0-beta.2:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
engines: {node: '>=6.9.0'}
get-caller-file@2.0.5:
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
engines: {node: 6.* || 8.* || >= 10.*}
get-intrinsic@1.3.0:
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
engines: {node: '>= 0.4'}
get-package-type@0.1.0:
resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==}
engines: {node: '>=8.0.0'}
get-proto@1.0.1:
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
engines: {node: '>= 0.4'}
get-stdin@6.0.0:
resolution: {integrity: sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==}
engines: {node: '>=4'}
get-stream@6.0.1:
resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
engines: {node: '>=10'}
get-symbol-description@1.1.0:
resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==}
engines: {node: '>= 0.4'}
git-hooks-list@1.0.3:
resolution: {integrity: sha512-Y7wLWcrLUXwk2noSka166byGCvhMtDRpgHdzCno1UQv/n/Hegp++a2xBWJL1lJarnKD3SWaljD+0z1ztqxuKyQ==}
glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
glob@7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
deprecated: Glob versions prior to v9 are no longer supported
glob@8.1.0:
resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
engines: {node: '>=12'}
deprecated: Glob versions prior to v9 are no longer supported
globals@11.12.0:
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
engines: {node: '>=4'}
globals@12.4.0:
resolution: {integrity: sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==}
engines: {node: '>=8'}
globalthis@1.0.4:
resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
engines: {node: '>= 0.4'}
globby@10.0.0:
resolution: {integrity: sha512-3LifW9M4joGZasyYPz2A1U74zbC/45fvpXUvO/9KbSa+VV0aGZarWkfdgKyR9sExNP0t0x0ss/UMJpNpcaTspw==}
engines: {node: '>=8'}
gopd@1.2.0:
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
engines: {node: '>= 0.4'}
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
has-bigints@1.1.0:
resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==}
engines: {node: '>= 0.4'}
has-flag@3.0.0:
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
engines: {node: '>=4'}
has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
has-property-descriptors@1.0.2:
resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
has-proto@1.2.0:
resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==}
engines: {node: '>= 0.4'}
has-symbols@1.1.0:
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
engines: {node: '>= 0.4'}
has-tostringtag@1.0.2:
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
engines: {node: '>= 0.4'}
has@1.0.4:
resolution: {integrity: sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==}
engines: {node: '>= 0.4.0'}
hasha@5.2.2:
resolution: {integrity: sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==}
engines: {node: '>=8'}
hasown@2.0.2:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
hosted-git-info@2.8.9:
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
html-encoding-sniffer@2.0.1:
resolution: {integrity: sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==}
engines: {node: '>=10'}
html-escaper@2.0.2:
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
http-assert@1.5.0:
resolution: {integrity: sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==}
engines: {node: '>= 0.8'}
http-errors@1.8.1:
resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==}
engines: {node: '>= 0.6'}
http-errors@2.0.0:
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
engines: {node: '>= 0.8'}
http-proxy-agent@4.0.1:
resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==}
engines: {node: '>= 6'}
https-proxy-agent@5.0.1:
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
engines: {node: '>= 6'}
human-signals@2.1.0:
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
engines: {node: '>=10.17.0'}
iconv-lite@0.4.24:
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
engines: {node: '>=0.10.0'}
ignore@4.0.6:
resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==}
engines: {node: '>= 4'}
ignore@5.3.2:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
import-fresh@3.3.1:
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
engines: {node: '>=6'}
import-local@3.2.0:
resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==}
engines: {node: '>=8'}
hasBin: true
imurmurhash@0.1.4:
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
engines: {node: '>=0.8.19'}
indent-string@4.0.0:
resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
engines: {node: '>=8'}
inflight@1.0.6:
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
inquirer@7.3.3:
resolution: {integrity: sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==}
engines: {node: '>=8.0.0'}
internal-slot@1.1.0:
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
engines: {node: '>= 0.4'}
ipaddr.js@1.9.1:
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
engines: {node: '>= 0.10'}
is-array-buffer@3.0.5:
resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==}
engines: {node: '>= 0.4'}
is-arrayish@0.2.1:
resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
is-async-function@2.1.1:
resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==}
engines: {node: '>= 0.4'}
is-bigint@1.1.0:
resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==}
engines: {node: '>= 0.4'}
is-boolean-object@1.2.2:
resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==}
engines: {node: '>= 0.4'}
is-callable@1.2.7:
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
engines: {node: '>= 0.4'}
is-core-module@2.16.1:
resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
engines: {node: '>= 0.4'}
is-data-view@1.0.2:
resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==}
engines: {node: '>= 0.4'}
is-date-object@1.1.0:
resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==}
engines: {node: '>= 0.4'}
is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
is-finalizationregistry@1.1.1:
resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==}
engines: {node: '>= 0.4'}
is-fullwidth-code-point@2.0.0:
resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==}
engines: {node: '>=4'}
is-fullwidth-code-point@3.0.0:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
is-generator-fn@2.1.0:
resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==}
engines: {node: '>=6'}
is-generator-function@1.1.0:
resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==}
engines: {node: '>= 0.4'}
is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
is-map@2.0.3:
resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
engines: {node: '>= 0.4'}
is-module@1.0.0:
resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==}
is-number-object@1.1.1:
resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==}
engines: {node: '>= 0.4'}
is-number@7.0.0:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'}
is-plain-obj@2.1.0:
resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==}
engines: {node: '>=8'}
is-potential-custom-element-name@1.0.1:
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
is-reference@1.2.1:
resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==}
is-regex@1.2.1:
resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
engines: {node: '>= 0.4'}
is-set@2.0.3:
resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==}
engines: {node: '>= 0.4'}
is-shared-array-buffer@1.0.4:
resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==}
engines: {node: '>= 0.4'}
is-stream@2.0.1:
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
engines: {node: '>=8'}
is-string@1.1.1:
resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==}
engines: {node: '>= 0.4'}
is-symbol@1.1.1:
resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==}
engines: {node: '>= 0.4'}
is-typed-array@1.1.15:
resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
engines: {node: '>= 0.4'}
is-typedarray@1.0.0:
resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==}
is-weakmap@2.0.2:
resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==}
engines: {node: '>= 0.4'}
is-weakref@1.1.1:
resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==}
engines: {node: '>= 0.4'}
is-weakset@2.0.4:
resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==}
engines: {node: '>= 0.4'}
is-windows@1.0.2:
resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==}
engines: {node: '>=0.10.0'}
isarray@1.0.0:
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
isarray@2.0.5:
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
istanbul-lib-coverage@3.2.2:
resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
engines: {node: '>=8'}
istanbul-lib-hook@3.0.0:
resolution: {integrity: sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==}
engines: {node: '>=8'}
istanbul-lib-instrument@4.0.3:
resolution: {integrity: sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==}
engines: {node: '>=8'}
istanbul-lib-instrument@5.2.1:
resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==}
engines: {node: '>=8'}
istanbul-lib-processinfo@2.0.3:
resolution: {integrity: sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==}
engines: {node: '>=8'}
istanbul-lib-report@3.0.1:
resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==}
engines: {node: '>=10'}
istanbul-lib-source-maps@4.0.1:
resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==}
engines: {node: '>=10'}
istanbul-reports@3.1.7:
resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==}
engines: {node: '>=8'}
jest-changed-files@27.5.1:
resolution: {integrity: sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-circus@27.5.1:
resolution: {integrity: sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-cli@27.5.1:
resolution: {integrity: sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
hasBin: true
peerDependencies:
node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
peerDependenciesMeta:
node-notifier:
optional: true
jest-config@27.5.1:
resolution: {integrity: sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
peerDependencies:
ts-node: '>=9.0.0'
peerDependenciesMeta:
ts-node:
optional: true
jest-diff@27.5.1:
resolution: {integrity: sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-docblock@27.5.1:
resolution: {integrity: sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-each@27.5.1:
resolution: {integrity: sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-environment-jsdom@27.5.1:
resolution: {integrity: sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-environment-node@27.5.1:
resolution: {integrity: sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-get-type@27.5.1:
resolution: {integrity: sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-haste-map@27.5.1:
resolution: {integrity: sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-jasmine2@27.5.1:
resolution: {integrity: sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-leak-detector@27.5.1:
resolution: {integrity: sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-matcher-utils@27.5.1:
resolution: {integrity: sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-message-util@27.5.1:
resolution: {integrity: sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-mock@27.5.1:
resolution: {integrity: sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-pnp-resolver@1.2.3:
resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==}
engines: {node: '>=6'}
peerDependencies:
jest-resolve: '*'
peerDependenciesMeta:
jest-resolve:
optional: true
jest-regex-util@27.5.1:
resolution: {integrity: sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-resolve-dependencies@27.5.1:
resolution: {integrity: sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-resolve@27.5.1:
resolution: {integrity: sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-runner@27.5.1:
resolution: {integrity: sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-runtime@27.5.1:
resolution: {integrity: sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-serializer@27.5.1:
resolution: {integrity: sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-snapshot@27.5.1:
resolution: {integrity: sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-util@27.5.1:
resolution: {integrity: sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-validate@27.5.1:
resolution: {integrity: sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-watcher@27.5.1:
resolution: {integrity: sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
jest-worker@27.5.1:
resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
engines: {node: '>= 10.13.0'}
jest@27.2.4:
resolution: {integrity: sha512-h4uqb1EQLfPulWyUFFWv9e9Nn8sCqsJ/j3wk/KCY0p4s4s0ICCfP3iMf6hRf5hEhsDyvyrCgKiZXma63gMz16A==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
hasBin: true
peerDependencies:
node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
peerDependenciesMeta:
node-notifier:
optional: true
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
js-yaml@3.14.1:
resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
hasBin: true
jsdom@16.7.0:
resolution: {integrity: sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==}
engines: {node: '>=10'}
peerDependencies:
canvas: ^2.5.0
peerDependenciesMeta:
canvas:
optional: true
jsesc@3.1.0:
resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
engines: {node: '>=6'}
hasBin: true
json-parse-even-better-errors@2.3.1:
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
json-schema-traverse@0.4.1:
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
json-stable-stringify-without-jsonify@1.0.1:
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
json5@2.2.3:
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
engines: {node: '>=6'}
hasBin: true
keygrip@1.1.0:
resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==}
engines: {node: '>= 0.6'}
kleur@3.0.3:
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
engines: {node: '>=6'}
koa-compose@4.1.0:
resolution: {integrity: sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==}
koa-convert@2.0.0:
resolution: {integrity: sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==}
engines: {node: '>= 10'}
koa@2.16.1:
resolution: {integrity: sha512-umfX9d3iuSxTQP4pnzLOz0HKnPg0FaUUIKcye2lOiz3KPu1Y3M3xlz76dISdFPQs37P9eJz1wUpcTS6KDPn9fA==}
engines: {node: ^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4}
leven@3.1.0:
resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
engines: {node: '>=6'}
levn@0.3.0:
resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==}
engines: {node: '>= 0.8.0'}
lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
load-json-file@2.0.0:
resolution: {integrity: sha512-3p6ZOGNbiX4CdvEd1VcE6yi78UrGNpjHO33noGwHCnT/o2fyllJDepsm8+mFFv/DvtwFHht5HIHSyOy5a+ChVQ==}
engines: {node: '>=4'}
locate-path@2.0.0:
resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==}
engines: {node: '>=4'}
locate-path@5.0.0:
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
engines: {node: '>=8'}
lodash.flattendeep@4.4.0:
resolution: {integrity: sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==}
lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
magic-string@0.30.17:
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
make-dir@3.1.0:
resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
engines: {node: '>=8'}
make-dir@4.0.0:
resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
engines: {node: '>=10'}
makeerror@1.0.12:
resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
media-typer@0.3.0:
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
engines: {node: '>= 0.6'}
merge-descriptors@1.0.3:
resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==}
merge-stream@2.0.0:
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
methods@1.1.2:
resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
engines: {node: '>= 0.6'}
micromatch@4.0.8:
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
engines: {node: '>=8.6'}
mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
mime-types@2.1.35:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
mime@1.6.0:
resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
engines: {node: '>=4'}
hasBin: true
mime@2.6.0:
resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==}
engines: {node: '>=4.0.0'}
hasBin: true
mimic-fn@2.1.0:
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
engines: {node: '>=6'}
minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
minimatch@5.1.6:
resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
engines: {node: '>=10'}
minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
mkdirp@0.5.6:
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
hasBin: true
ms@2.0.0:
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
mute-stream@0.0.8:
resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==}
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
negotiator@0.6.3:
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
engines: {node: '>= 0.6'}
nice-try@1.0.5:
resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'}
deprecated: Use your platform's native DOMException instead
node-int64@0.4.0:
resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
node-preload@0.2.1:
resolution: {integrity: sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==}
engines: {node: '>=8'}
node-releases@2.0.19:
resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
normalize-package-data@2.5.0:
resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
npm-run-path@4.0.1:
resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
engines: {node: '>=8'}
nwsapi@2.2.20:
resolution: {integrity: sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==}
nyc@15.1.0:
resolution: {integrity: sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==}
engines: {node: '>=8.9'}
hasBin: true
object-inspect@1.13.4:
resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
engines: {node: '>= 0.4'}
object-keys@1.1.1:
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
engines: {node: '>= 0.4'}
object.assign@4.1.7:
resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==}
engines: {node: '>= 0.4'}
object.entries@1.1.9:
resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==}
engines: {node: '>= 0.4'}
object.values@1.2.1:
resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==}
engines: {node: '>= 0.4'}
on-finished@2.4.1:
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
engines: {node: '>= 0.8'}
once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
onetime@5.1.2:
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
engines: {node: '>=6'}
only@0.0.2:
resolution: {integrity: sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==}
optionator@0.8.3:
resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==}
engines: {node: '>= 0.8.0'}
os-tmpdir@1.0.2:
resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==}
engines: {node: '>=0.10.0'}
own-keys@1.0.1:
resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
engines: {node: '>= 0.4'}
p-limit@1.3.0:
resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==}
engines: {node: '>=4'}
p-limit@2.3.0:
resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
engines: {node: '>=6'}
p-locate@2.0.0:
resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==}
engines: {node: '>=4'}
p-locate@4.1.0:
resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
engines: {node: '>=8'}
p-map@3.0.0:
resolution: {integrity: sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==}
engines: {node: '>=8'}
p-try@1.0.0:
resolution: {integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==}
engines: {node: '>=4'}
p-try@2.2.0:
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
engines: {node: '>=6'}
package-hash@4.0.0:
resolution: {integrity: sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==}
engines: {node: '>=8'}
parent-module@1.0.1:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'}
parse-json@2.2.0:
resolution: {integrity: sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==}
engines: {node: '>=0.10.0'}
parse-json@5.2.0:
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
engines: {node: '>=8'}
parse5@6.0.1:
resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
parseurl@1.3.3:
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
engines: {node: '>= 0.8'}
path-exists@3.0.0:
resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==}
engines: {node: '>=4'}
path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
path-is-absolute@1.0.1:
resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
engines: {node: '>=0.10.0'}
path-key@2.0.1:
resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==}
engines: {node: '>=4'}
path-key@3.1.1:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
path-parse@1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
path-to-regexp@0.1.12:
resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==}
path-type@2.0.0:
resolution: {integrity: sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ==}
engines: {node: '>=4'}
path-type@4.0.0:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
picomatch@4.0.2:
resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
engines: {node: '>=12'}
pify@2.3.0:
resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
engines: {node: '>=0.10.0'}
pirates@4.0.7:
resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
engines: {node: '>= 6'}
pkg-dir@4.2.0:
resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
engines: {node: '>=8'}
possible-typed-array-names@1.1.0:
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
engines: {node: '>= 0.4'}
prelude-ls@1.1.2:
resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==}
engines: {node: '>= 0.8.0'}
prettier-linter-helpers@1.0.0:
resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
engines: {node: '>=6.0.0'}
prettier-plugin-pkgjson@0.2.8:
resolution: {integrity: sha512-MBpPCjqQKxKc5SxhLkVeE2Q+3N7KBk+zJLkFzHVL6SMRAZlyiq/44w/NonCmO+24pI7s/LKoeFqcWY2+08vuEg==}
engines: {node: '>=10.13'}
peerDependencies:
prettier: ^2.0.2
prettier@2.0.5:
resolution: {integrity: sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==}
engines: {node: '>=10.13.0'}
hasBin: true
pretty-format@27.5.1:
resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
process-on-spawn@1.1.0:
resolution: {integrity: sha512-JOnOPQ/8TZgjs1JIH/m9ni7FfimjNa/PRx7y/Wb5qdItsnhO0jE4AT7fC0HjC28DUQWDr50dwSYZLdRMlqDq3Q==}
engines: {node: '>=8'}
progress@2.0.3:
resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
engines: {node: '>=0.4.0'}
prompts@2.4.2:
resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
engines: {node: '>= 6'}
proxy-addr@2.0.7:
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
engines: {node: '>= 0.10'}
psl@1.15.0:
resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==}
punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
qs@6.13.0:
resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
engines: {node: '>=0.6'}
qs@6.14.0:
resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
engines: {node: '>=0.6'}
querystringify@2.2.0:
resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
range-parser@1.2.1:
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
engines: {node: '>= 0.6'}
raw-body@2.5.2:
resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
engines: {node: '>= 0.8'}
react-is@17.0.2:
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
read-pkg-up@2.0.0:
resolution: {integrity: sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w==}
engines: {node: '>=4'}
read-pkg@2.0.0:
resolution: {integrity: sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA==}
engines: {node: '>=4'}
readable-stream@3.6.2:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
engines: {node: '>= 6'}
reflect.getprototypeof@1.0.10:
resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
engines: {node: '>= 0.4'}
regexp.prototype.flags@1.5.4:
resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
engines: {node: '>= 0.4'}
regexpp@2.0.1:
resolution: {integrity: sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==}
engines: {node: '>=6.5.0'}
release-zalgo@1.0.0:
resolution: {integrity: sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==}
engines: {node: '>=4'}
require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
require-main-filename@2.0.0:
resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
requires-port@1.0.0:
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
resolve-cwd@3.0.0:
resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==}
engines: {node: '>=8'}
resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
resolve-from@5.0.0:
resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
engines: {node: '>=8'}
resolve.exports@1.1.1:
resolution: {integrity: sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==}
engines: {node: '>=10'}
resolve@1.22.10:
resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
engines: {node: '>= 0.4'}
hasBin: true
restore-cursor@3.1.0:
resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
engines: {node: '>=8'}
reusify@1.1.0:
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
rimraf@2.6.3:
resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==}
deprecated: Rimraf versions prior to v4 are no longer supported
hasBin: true
rimraf@3.0.2:
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
deprecated: Rimraf versions prior to v4 are no longer supported
hasBin: true
rollup@3.29.5:
resolution: {integrity: sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==}
engines: {node: '>=14.18.0', npm: '>=8.0.0'}
hasBin: true
run-async@2.4.1:
resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==}
engines: {node: '>=0.12.0'}
run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
rxjs@6.6.7:
resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==}
engines: {npm: '>=2.0.0'}
safe-array-concat@1.1.3:
resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==}
engines: {node: '>=0.4'}
safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
safe-push-apply@1.0.0:
resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==}
engines: {node: '>= 0.4'}
safe-regex-test@1.1.0:
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
engines: {node: '>= 0.4'}
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
saxes@5.0.1:
resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==}
engines: {node: '>=10'}
semver@5.7.2:
resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
hasBin: true
semver@6.3.1:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
semver@7.7.1:
resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==}
engines: {node: '>=10'}
hasBin: true
send@0.19.0:
resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==}
engines: {node: '>= 0.8.0'}
serve-static@1.16.2:
resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==}
engines: {node: '>= 0.8.0'}
set-blocking@2.0.0:
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
set-function-length@1.2.2:
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
engines: {node: '>= 0.4'}
set-function-name@2.0.2:
resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
engines: {node: '>= 0.4'}
set-proto@1.0.0:
resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
engines: {node: '>= 0.4'}
setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
shebang-command@1.2.0:
resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
engines: {node: '>=0.10.0'}
shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
shebang-regex@1.0.0:
resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==}
engines: {node: '>=0.10.0'}
shebang-regex@3.0.0:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
side-channel-list@1.0.0:
resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
engines: {node: '>= 0.4'}
side-channel-map@1.0.1:
resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==}
engines: {node: '>= 0.4'}
side-channel-weakmap@1.0.2:
resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
engines: {node: '>= 0.4'}
side-channel@1.1.0:
resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
engines: {node: '>= 0.4'}
signal-exit@3.0.7:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
sisteransi@1.0.5:
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
slash@3.0.0:
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
engines: {node: '>=8'}
slice-ansi@2.1.0:
resolution: {integrity: sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==}
engines: {node: '>=6'}
sort-object-keys@1.1.3:
resolution: {integrity: sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==}
sort-package-json@1.57.0:
resolution: {integrity: sha512-FYsjYn2dHTRb41wqnv+uEqCUvBpK3jZcTp9rbz2qDTmel7Pmdtf+i2rLaaPMRZeSVM60V3Se31GyWFpmKs4Q5Q==}
hasBin: true
source-map-support@0.5.21:
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
source-map@0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
source-map@0.7.4:
resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
engines: {node: '>= 8'}
spawn-wrap@2.0.0:
resolution: {integrity: sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==}
engines: {node: '>=8'}
spdx-correct@3.2.0:
resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==}
spdx-exceptions@2.5.0:
resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==}
spdx-expression-parse@3.0.1:
resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==}
spdx-license-ids@3.0.21:
resolution: {integrity: sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==}
sprintf-js@1.0.3:
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
stack-utils@2.0.6:
resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
engines: {node: '>=10'}
statuses@1.5.0:
resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==}
engines: {node: '>= 0.6'}
statuses@2.0.1:
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
engines: {node: '>= 0.8'}
string-length@4.0.2:
resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==}
engines: {node: '>=10'}
string-width@3.1.0:
resolution: {integrity: sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==}
engines: {node: '>=6'}
string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
string.prototype.trim@1.2.10:
resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==}
engines: {node: '>= 0.4'}
string.prototype.trimend@1.0.9:
resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==}
engines: {node: '>= 0.4'}
string.prototype.trimstart@1.0.8:
resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
engines: {node: '>= 0.4'}
string_decoder@1.3.0:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
strip-ansi@5.2.0:
resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==}
engines: {node: '>=6'}
strip-ansi@6.0.1:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'}
strip-bom@3.0.0:
resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
engines: {node: '>=4'}
strip-bom@4.0.0:
resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==}
engines: {node: '>=8'}
strip-final-newline@2.0.0:
resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
engines: {node: '>=6'}
strip-json-comments@3.1.1:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
superagent@6.1.0:
resolution: {integrity: sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==}
engines: {node: '>= 7.0.0'}
deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net
supertest@6.1.6:
resolution: {integrity: sha512-0hACYGNJ8OHRg8CRITeZOdbjur7NLuNs0mBjVhdpxi7hP6t3QIbOzLON5RTUmZcy2I9riuII3+Pr2C7yztrIIg==}
engines: {node: '>=6.0.0'}
supports-color@5.5.0:
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
engines: {node: '>=4'}
supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
supports-color@8.1.1:
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
engines: {node: '>=10'}
supports-hyperlinks@2.3.0:
resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==}
engines: {node: '>=8'}
supports-preserve-symlinks-flag@1.0.0:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
symbol-tree@3.2.4:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
table@5.4.6:
resolution: {integrity: sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==}
engines: {node: '>=6.0.0'}
terminal-link@2.1.1:
resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==}
engines: {node: '>=8'}
test-exclude@6.0.0:
resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
engines: {node: '>=8'}
text-table@0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
throat@6.0.2:
resolution: {integrity: sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==}
through@2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
tmp@0.0.33:
resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
engines: {node: '>=0.6.0'}
tmpl@1.0.5:
resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}
to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
toidentifier@1.0.1:
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
engines: {node: '>=0.6'}
tough-cookie@4.1.4:
resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==}
engines: {node: '>=6'}
tr46@2.1.0:
resolution: {integrity: sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==}
engines: {node: '>=8'}
tslib@1.14.1:
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
tsscmp@1.0.6:
resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==}
engines: {node: '>=0.6.x'}
type-check@0.3.2:
resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==}
engines: {node: '>= 0.8.0'}
type-detect@4.0.8:
resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
engines: {node: '>=4'}
type-fest@0.21.3:
resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
engines: {node: '>=10'}
type-fest@0.8.1:
resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==}
engines: {node: '>=8'}
type-is@1.6.18:
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
engines: {node: '>= 0.6'}
typed-array-buffer@1.0.3:
resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
engines: {node: '>= 0.4'}
typed-array-byte-length@1.0.3:
resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==}
engines: {node: '>= 0.4'}
typed-array-byte-offset@1.0.4:
resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==}
engines: {node: '>= 0.4'}
typed-array-length@1.0.7:
resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==}
engines: {node: '>= 0.4'}
typedarray-to-buffer@3.1.5:
resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==}
unbox-primitive@1.1.0:
resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
engines: {node: '>= 0.4'}
undici-types@6.21.0:
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
universalify@0.2.0:
resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
engines: {node: '>= 4.0.0'}
unpipe@1.0.0:
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
engines: {node: '>= 0.8'}
update-browserslist-db@1.1.3:
resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
hasBin: true
peerDependencies:
browserslist: '>= 4.21.0'
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
url-parse@1.5.10:
resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
utils-merge@1.0.1:
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
engines: {node: '>= 0.4.0'}
uuid@8.3.2:
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
hasBin: true
v8-compile-cache@2.4.0:
resolution: {integrity: sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==}
v8-to-istanbul@8.1.1:
resolution: {integrity: sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==}
engines: {node: '>=10.12.0'}
validate-npm-package-license@3.0.4:
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
vary@1.1.2:
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
engines: {node: '>= 0.8'}
w3c-hr-time@1.0.2:
resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==}
deprecated: Use your platform's native performance.now() and performance.timeOrigin.
w3c-xmlserializer@2.0.0:
resolution: {integrity: sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==}
engines: {node: '>=10'}
walker@1.0.8:
resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
web-streams-polyfill@3.3.3:
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
engines: {node: '>= 8'}
webidl-conversions@5.0.0:
resolution: {integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==}
engines: {node: '>=8'}
webidl-conversions@6.1.0:
resolution: {integrity: sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==}
engines: {node: '>=10.4'}
whatwg-encoding@1.0.5:
resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==}
whatwg-mimetype@2.3.0:
resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==}
whatwg-url@8.7.0:
resolution: {integrity: sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==}
engines: {node: '>=10'}
which-boxed-primitive@1.1.1:
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
engines: {node: '>= 0.4'}
which-builtin-type@1.2.1:
resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==}
engines: {node: '>= 0.4'}
which-collection@1.0.2:
resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==}
engines: {node: '>= 0.4'}
which-module@2.0.1:
resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
which-typed-array@1.1.19:
resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==}
engines: {node: '>= 0.4'}
which@1.3.1:
resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
hasBin: true
which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
hasBin: true
word-wrap@1.2.5:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
wrap-ansi@6.2.0:
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
engines: {node: '>=8'}
wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
write-file-atomic@3.0.3:
resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==}
write@1.0.3:
resolution: {integrity: sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==}
engines: {node: '>=4'}
ws@7.5.10:
resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==}
engines: {node: '>=8.3.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: ^5.0.2
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
xml-name-validator@3.0.0:
resolution: {integrity: sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==}
xmlchars@2.2.0:
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
y18n@4.0.3:
resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
y18n@5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}
yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
yargs-parser@18.1.3:
resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
engines: {node: '>=6'}
yargs-parser@20.2.9:
resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
engines: {node: '>=10'}
yargs@15.4.1:
resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
engines: {node: '>=8'}
yargs@16.2.0:
resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
engines: {node: '>=10'}
ylru@1.4.0:
resolution: {integrity: sha512-2OQsPNEmBCvXuFlIni/a+Rn+R2pHW9INm0BxXJ4hVDA8TirqMj+J/Rp9ItLatT/5pZqWwefVrTQcHpixsxnVlA==}
engines: {node: '>= 4.0.0'}
snapshots:
'@ampproject/remapping@2.3.0':
dependencies:
'@jridgewell/gen-mapping': 0.3.8
'@jridgewell/trace-mapping': 0.3.25
'@babel/code-frame@7.26.2':
dependencies:
'@babel/helper-validator-identifier': 7.25.9
js-tokens: 4.0.0
picocolors: 1.1.1
'@babel/compat-data@7.26.8': {}
'@babel/core@7.26.10':
dependencies:
'@ampproject/remapping': 2.3.0
'@babel/code-frame': 7.26.2
'@babel/generator': 7.27.0
'@babel/helper-compilation-targets': 7.27.0
'@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.10)
'@babel/helpers': 7.27.0
'@babel/parser': 7.27.0
'@babel/template': 7.27.0
'@babel/traverse': 7.27.0
'@babel/types': 7.27.0
convert-source-map: 2.0.0
debug: 4.4.0
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
transitivePeerDependencies:
- supports-color
'@babel/generator@7.27.0':
dependencies:
'@babel/parser': 7.27.0
'@babel/types': 7.27.0
'@jridgewell/gen-mapping': 0.3.8
'@jridgewell/trace-mapping': 0.3.25
jsesc: 3.1.0
'@babel/helper-compilation-targets@7.27.0':
dependencies:
'@babel/compat-data': 7.26.8
'@babel/helper-validator-option': 7.25.9
browserslist: 4.24.4
lru-cache: 5.1.1
semver: 6.3.1
'@babel/helper-module-imports@7.25.9':
dependencies:
'@babel/traverse': 7.27.0
'@babel/types': 7.27.0
transitivePeerDependencies:
- supports-color
'@babel/helper-module-transforms@7.26.0(@babel/core@7.26.10)':
dependencies:
'@babel/core': 7.26.10
'@babel/helper-module-imports': 7.25.9
'@babel/helper-validator-identifier': 7.25.9
'@babel/traverse': 7.27.0
transitivePeerDependencies:
- supports-color
'@babel/helper-plugin-utils@7.26.5': {}
'@babel/helper-string-parser@7.25.9': {}
'@babel/helper-validator-identifier@7.25.9': {}
'@babel/helper-validator-option@7.25.9': {}
'@babel/helpers@7.27.0':
dependencies:
'@babel/template': 7.27.0
'@babel/types': 7.27.0
'@babel/parser@7.27.0':
dependencies:
'@babel/types': 7.27.0
'@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.26.10)':
dependencies:
'@babel/core': 7.26.10
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.26.10)':
dependencies:
'@babel/core': 7.26.10
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.26.10)':
dependencies:
'@babel/core': 7.26.10
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.26.10)':
dependencies:
'@babel/core': 7.26.10
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-syntax-import-attributes@7.26.0(@babel/core@7.26.10)':
dependencies:
'@babel/core': 7.26.10
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.26.10)':
dependencies:
'@babel/core': 7.26.10
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.26.10)':
dependencies:
'@babel/core': 7.26.10
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.26.10)':
dependencies:
'@babel/core': 7.26.10
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.26.10)':
dependencies:
'@babel/core': 7.26.10
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.26.10)':
dependencies:
'@babel/core': 7.26.10
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.26.10)':
dependencies:
'@babel/core': 7.26.10
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.26.10)':
dependencies:
'@babel/core': 7.26.10
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.26.10)':
dependencies:
'@babel/core': 7.26.10
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.26.10)':
dependencies:
'@babel/core': 7.26.10
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.26.10)':
dependencies:
'@babel/core': 7.26.10
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.10)':
dependencies:
'@babel/core': 7.26.10
'@babel/helper-plugin-utils': 7.26.5
'@babel/template@7.27.0':
dependencies:
'@babel/code-frame': 7.26.2
'@babel/parser': 7.27.0
'@babel/types': 7.27.0
'@babel/traverse@7.27.0':
dependencies:
'@babel/code-frame': 7.26.2
'@babel/generator': 7.27.0
'@babel/parser': 7.27.0
'@babel/template': 7.27.0
'@babel/types': 7.27.0
debug: 4.4.0
globals: 11.12.0
transitivePeerDependencies:
- supports-color
'@babel/types@7.27.0':
dependencies:
'@babel/helper-string-parser': 7.25.9
'@babel/helper-validator-identifier': 7.25.9
'@bcoe/v8-coverage@0.2.3': {}
'@istanbuljs/load-nyc-config@1.1.0':
dependencies:
camelcase: 5.3.1
find-up: 4.1.0
get-package-type: 0.1.0
js-yaml: 3.14.1
resolve-from: 5.0.0
'@istanbuljs/schema@0.1.3': {}
'@jest/console@27.5.1':
dependencies:
'@jest/types': 27.5.1
'@types/node': 22.14.1
chalk: 4.1.2
jest-message-util: 27.5.1
jest-util: 27.5.1
slash: 3.0.0
'@jest/core@27.5.1':
dependencies:
'@jest/console': 27.5.1
'@jest/reporters': 27.5.1
'@jest/test-result': 27.5.1
'@jest/transform': 27.5.1
'@jest/types': 27.5.1
'@types/node': 22.14.1
ansi-escapes: 4.3.2
chalk: 4.1.2
emittery: 0.8.1
exit: 0.1.2
graceful-fs: 4.2.11
jest-changed-files: 27.5.1
jest-config: 27.5.1
jest-haste-map: 27.5.1
jest-message-util: 27.5.1
jest-regex-util: 27.5.1
jest-resolve: 27.5.1
jest-resolve-dependencies: 27.5.1
jest-runner: 27.5.1
jest-runtime: 27.5.1
jest-snapshot: 27.5.1
jest-util: 27.5.1
jest-validate: 27.5.1
jest-watcher: 27.5.1
micromatch: 4.0.8
rimraf: 3.0.2
slash: 3.0.0
strip-ansi: 6.0.1
transitivePeerDependencies:
- bufferutil
- canvas
- supports-color
- ts-node
- utf-8-validate
'@jest/environment@27.5.1':
dependencies:
'@jest/fake-timers': 27.5.1
'@jest/types': 27.5.1
'@types/node': 22.14.1
jest-mock: 27.5.1
'@jest/fake-timers@27.5.1':
dependencies:
'@jest/types': 27.5.1
'@sinonjs/fake-timers': 8.1.0
'@types/node': 22.14.1
jest-message-util: 27.5.1
jest-mock: 27.5.1
jest-util: 27.5.1
'@jest/globals@27.5.1':
dependencies:
'@jest/environment': 27.5.1
'@jest/types': 27.5.1
expect: 27.5.1
'@jest/reporters@27.5.1':
dependencies:
'@bcoe/v8-coverage': 0.2.3
'@jest/console': 27.5.1
'@jest/test-result': 27.5.1
'@jest/transform': 27.5.1
'@jest/types': 27.5.1
'@types/node': 22.14.1
chalk: 4.1.2
collect-v8-coverage: 1.0.2
exit: 0.1.2
glob: 7.2.3
graceful-fs: 4.2.11
istanbul-lib-coverage: 3.2.2
istanbul-lib-instrument: 5.2.1
istanbul-lib-report: 3.0.1
istanbul-lib-source-maps: 4.0.1
istanbul-reports: 3.1.7
jest-haste-map: 27.5.1
jest-resolve: 27.5.1
jest-util: 27.5.1
jest-worker: 27.5.1
slash: 3.0.0
source-map: 0.6.1
string-length: 4.0.2
terminal-link: 2.1.1
v8-to-istanbul: 8.1.1
transitivePeerDependencies:
- supports-color
'@jest/source-map@27.5.1':
dependencies:
callsites: 3.1.0
graceful-fs: 4.2.11
source-map: 0.6.1
'@jest/test-result@27.5.1':
dependencies:
'@jest/console': 27.5.1
'@jest/types': 27.5.1
'@types/istanbul-lib-coverage': 2.0.6
collect-v8-coverage: 1.0.2
'@jest/test-sequencer@27.5.1':
dependencies:
'@jest/test-result': 27.5.1
graceful-fs: 4.2.11
jest-haste-map: 27.5.1
jest-runtime: 27.5.1
transitivePeerDependencies:
- supports-color
'@jest/transform@27.5.1':
dependencies:
'@babel/core': 7.26.10
'@jest/types': 27.5.1
babel-plugin-istanbul: 6.1.1
chalk: 4.1.2
convert-source-map: 1.9.0
fast-json-stable-stringify: 2.1.0
graceful-fs: 4.2.11
jest-haste-map: 27.5.1
jest-regex-util: 27.5.1
jest-util: 27.5.1
micromatch: 4.0.8
pirates: 4.0.7
slash: 3.0.0
source-map: 0.6.1
write-file-atomic: 3.0.3
transitivePeerDependencies:
- supports-color
'@jest/types@27.5.1':
dependencies:
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4
'@types/node': 22.14.1
'@types/yargs': 16.0.9
chalk: 4.1.2
'@jridgewell/gen-mapping@0.3.8':
dependencies:
'@jridgewell/set-array': 1.2.1
'@jridgewell/sourcemap-codec': 1.5.0
'@jridgewell/trace-mapping': 0.3.25
'@jridgewell/resolve-uri@3.1.2': {}
'@jridgewell/set-array@1.2.1': {}
'@jridgewell/sourcemap-codec@1.5.0': {}
'@jridgewell/trace-mapping@0.3.25':
dependencies:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.0
'@noble/hashes@1.8.0': {}
'@nodelib/fs.scandir@2.1.5':
dependencies:
'@nodelib/fs.stat': 2.0.5
run-parallel: 1.2.0
'@nodelib/fs.stat@2.0.5': {}
'@nodelib/fs.walk@1.2.8':
dependencies:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.19.1
'@paralleldrive/cuid2@2.2.2':
dependencies:
'@noble/hashes': 1.8.0
'@rollup/plugin-commonjs@25.0.8(rollup@3.29.5)':
dependencies:
'@rollup/pluginutils': 5.1.4(rollup@3.29.5)
commondir: 1.0.1
estree-walker: 2.0.2
glob: 8.1.0
is-reference: 1.2.1
magic-string: 0.30.17
optionalDependencies:
rollup: 3.29.5
'@rollup/plugin-node-resolve@15.3.1(rollup@3.29.5)':
dependencies:
'@rollup/pluginutils': 5.1.4(rollup@3.29.5)
'@types/resolve': 1.20.2
deepmerge: 4.3.1
is-module: 1.0.0
resolve: 1.22.10
optionalDependencies:
rollup: 3.29.5
'@rollup/pluginutils@5.1.4(rollup@3.29.5)':
dependencies:
'@types/estree': 1.0.7
estree-walker: 2.0.2
picomatch: 4.0.2
optionalDependencies:
rollup: 3.29.5
'@sindresorhus/slugify@2.2.1':
dependencies:
'@sindresorhus/transliterate': 1.6.0
escape-string-regexp: 5.0.0
'@sindresorhus/transliterate@1.6.0':
dependencies:
escape-string-regexp: 5.0.0
'@sinonjs/commons@1.8.6':
dependencies:
type-detect: 4.0.8
'@sinonjs/fake-timers@8.1.0':
dependencies:
'@sinonjs/commons': 1.8.6
'@tootallnate/once@1.1.2': {}
'@tunnckocore/prettier-config@1.3.8(prettier-plugin-pkgjson@0.2.8(prettier@2.0.5))(prettier@2.0.5)':
dependencies:
prettier: 2.0.5
prettier-plugin-pkgjson: 0.2.8(prettier@2.0.5)
'@types/babel__core@7.20.5':
dependencies:
'@babel/parser': 7.27.0
'@babel/types': 7.27.0
'@types/babel__generator': 7.27.0
'@types/babel__template': 7.4.4
'@types/babel__traverse': 7.20.7
'@types/babel__generator@7.27.0':
dependencies:
'@babel/types': 7.27.0
'@types/babel__template@7.4.4':
dependencies:
'@babel/parser': 7.27.0
'@babel/types': 7.27.0
'@types/babel__traverse@7.20.7':
dependencies:
'@babel/types': 7.27.0
'@types/estree@1.0.7': {}
'@types/glob@7.2.0':
dependencies:
'@types/minimatch': 5.1.2
'@types/node': 22.14.1
'@types/graceful-fs@4.1.9':
dependencies:
'@types/node': 22.14.1
'@types/istanbul-lib-coverage@2.0.6': {}
'@types/istanbul-lib-report@3.0.3':
dependencies:
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports@3.0.4':
dependencies:
'@types/istanbul-lib-report': 3.0.3
'@types/minimatch@5.1.2': {}
'@types/node@22.14.1':
dependencies:
undici-types: 6.21.0
'@types/prettier@2.7.3': {}
'@types/resolve@1.20.2': {}
'@types/stack-utils@2.0.3': {}
'@types/yargs-parser@21.0.3': {}
'@types/yargs@16.0.9':
dependencies:
'@types/yargs-parser': 21.0.3
abab@2.0.6: {}
accepts@1.3.8:
dependencies:
mime-types: 2.1.35
negotiator: 0.6.3
acorn-globals@6.0.0:
dependencies:
acorn: 7.4.1
acorn-walk: 7.2.0
acorn-jsx@5.3.2(acorn@7.4.1):
dependencies:
acorn: 7.4.1
acorn-walk@7.2.0: {}
acorn@7.4.1: {}
acorn@8.14.1: {}
agent-base@6.0.2:
dependencies:
debug: 4.4.0
transitivePeerDependencies:
- supports-color
aggregate-error@3.1.0:
dependencies:
clean-stack: 2.2.0
indent-string: 4.0.0
ajv@6.12.6:
dependencies:
fast-deep-equal: 3.1.3
fast-json-stable-stringify: 2.1.0
json-schema-traverse: 0.4.1
uri-js: 4.4.1
ansi-escapes@4.3.2:
dependencies:
type-fest: 0.21.3
ansi-regex@4.1.1: {}
ansi-regex@5.0.1: {}
ansi-styles@3.2.1:
dependencies:
color-convert: 1.9.3
ansi-styles@4.3.0:
dependencies:
color-convert: 2.0.1
ansi-styles@5.2.0: {}
anymatch@3.1.3:
dependencies:
normalize-path: 3.0.0
picomatch: 2.3.1
append-transform@2.0.0:
dependencies:
default-require-extensions: 3.0.1
archy@1.0.0: {}
argparse@1.0.10:
dependencies:
sprintf-js: 1.0.3
array-buffer-byte-length@1.0.2:
dependencies:
call-bound: 1.0.4
is-array-buffer: 3.0.5
array-flatten@1.1.1: {}
array-includes@3.1.8:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
es-abstract: 1.23.9
es-object-atoms: 1.1.1
get-intrinsic: 1.3.0
is-string: 1.1.1
array-union@2.1.0: {}
array.prototype.flat@1.3.3:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
es-abstract: 1.23.9
es-shim-unscopables: 1.1.0
arraybuffer.prototype.slice@1.0.4:
dependencies:
array-buffer-byte-length: 1.0.2
call-bind: 1.0.8
define-properties: 1.2.1
es-abstract: 1.23.9
es-errors: 1.3.0
get-intrinsic: 1.3.0
is-array-buffer: 3.0.5
asap@2.0.6: {}
astral-regex@1.0.0: {}
async-function@1.0.0: {}
asynckit@0.4.0: {}
available-typed-arrays@1.0.7:
dependencies:
possible-typed-array-names: 1.1.0
babel-jest@27.5.1(@babel/core@7.26.10):
dependencies:
'@babel/core': 7.26.10
'@jest/transform': 27.5.1
'@jest/types': 27.5.1
'@types/babel__core': 7.20.5
babel-plugin-istanbul: 6.1.1
babel-preset-jest: 27.5.1(@babel/core@7.26.10)
chalk: 4.1.2
graceful-fs: 4.2.11
slash: 3.0.0
transitivePeerDependencies:
- supports-color
babel-plugin-istanbul@6.1.1:
dependencies:
'@babel/helper-plugin-utils': 7.26.5
'@istanbuljs/load-nyc-config': 1.1.0
'@istanbuljs/schema': 0.1.3
istanbul-lib-instrument: 5.2.1
test-exclude: 6.0.0
transitivePeerDependencies:
- supports-color
babel-plugin-jest-hoist@27.5.1:
dependencies:
'@babel/template': 7.27.0
'@babel/types': 7.27.0
'@types/babel__core': 7.20.5
'@types/babel__traverse': 7.20.7
babel-preset-current-node-syntax@1.1.0(@babel/core@7.26.10):
dependencies:
'@babel/core': 7.26.10
'@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.26.10)
'@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.26.10)
'@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.26.10)
'@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.26.10)
'@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.10)
'@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.26.10)
'@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.26.10)
'@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.26.10)
'@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.26.10)
'@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.26.10)
'@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.26.10)
'@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.26.10)
'@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.26.10)
'@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.26.10)
'@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.26.10)
babel-preset-jest@27.5.1(@babel/core@7.26.10):
dependencies:
'@babel/core': 7.26.10
babel-plugin-jest-hoist: 27.5.1
babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.10)
balanced-match@1.0.2: {}
body-parser@1.20.3:
dependencies:
bytes: 3.1.2
content-type: 1.0.5
debug: 2.6.9
depd: 2.0.0
destroy: 1.2.0
http-errors: 2.0.0
iconv-lite: 0.4.24
on-finished: 2.4.1
qs: 6.13.0
raw-body: 2.5.2
type-is: 1.6.18
unpipe: 1.0.0
transitivePeerDependencies:
- supports-color
brace-expansion@1.1.11:
dependencies:
balanced-match: 1.0.2
concat-map: 0.0.1
brace-expansion@2.0.1:
dependencies:
balanced-match: 1.0.2
braces@3.0.3:
dependencies:
fill-range: 7.1.1
browser-process-hrtime@1.0.0: {}
browserslist@4.24.4:
dependencies:
caniuse-lite: 1.0.30001715
electron-to-chromium: 1.5.140
node-releases: 2.0.19
update-browserslist-db: 1.1.3(browserslist@4.24.4)
bser@2.1.1:
dependencies:
node-int64: 0.4.0
buffer-from@1.1.2: {}
bytes@3.1.2: {}
cache-content-type@1.0.1:
dependencies:
mime-types: 2.1.35
ylru: 1.4.0
caching-transform@4.0.0:
dependencies:
hasha: 5.2.2
make-dir: 3.1.0
package-hash: 4.0.0
write-file-atomic: 3.0.3
call-bind-apply-helpers@1.0.2:
dependencies:
es-errors: 1.3.0
function-bind: 1.1.2
call-bind@1.0.8:
dependencies:
call-bind-apply-helpers: 1.0.2
es-define-property: 1.0.1
get-intrinsic: 1.3.0
set-function-length: 1.2.2
call-bound@1.0.4:
dependencies:
call-bind-apply-helpers: 1.0.2
get-intrinsic: 1.3.0
callsites@3.1.0: {}
camelcase@5.3.1: {}
camelcase@6.3.0: {}
caniuse-lite@1.0.30001715: {}
chalk@2.4.2:
dependencies:
ansi-styles: 3.2.1
escape-string-regexp: 1.0.5
supports-color: 5.5.0
chalk@4.1.2:
dependencies:
ansi-styles: 4.3.0
supports-color: 7.2.0
char-regex@1.0.2: {}
chardet@0.7.0: {}
ci-info@3.9.0: {}
cjs-module-lexer@1.4.3: {}
clean-stack@2.2.0: {}
cli-cursor@3.1.0:
dependencies:
restore-cursor: 3.1.0
cli-width@3.0.0: {}
cliui@6.0.0:
dependencies:
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi: 6.2.0
cliui@7.0.4:
dependencies:
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi: 7.0.0
co@4.6.0: {}
collect-v8-coverage@1.0.2: {}
color-convert@1.9.3:
dependencies:
color-name: 1.1.3
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
color-name@1.1.3: {}
color-name@1.1.4: {}
combined-stream@1.0.8:
dependencies:
delayed-stream: 1.0.0
commondir@1.0.1: {}
component-emitter@1.3.1: {}
concat-map@0.0.1: {}
confusing-browser-globals@1.0.11: {}
contains-path@0.1.0: {}
content-disposition@0.5.4:
dependencies:
safe-buffer: 5.2.1
content-type@1.0.5: {}
convert-source-map@1.9.0: {}
convert-source-map@2.0.0: {}
cookie-signature@1.0.6: {}
cookie@0.7.1: {}
cookiejar@2.1.4: {}
cookies@0.9.1:
dependencies:
depd: 2.0.0
keygrip: 1.1.0
cross-spawn@6.0.6:
dependencies:
nice-try: 1.0.5
path-key: 2.0.1
semver: 5.7.2
shebang-command: 1.2.0
which: 1.3.1
cross-spawn@7.0.6:
dependencies:
path-key: 3.1.1
shebang-command: 2.0.0
which: 2.0.2
cssom@0.3.8: {}
cssom@0.4.4: {}
cssstyle@2.3.0:
dependencies:
cssom: 0.3.8
data-urls@2.0.0:
dependencies:
abab: 2.0.6
whatwg-mimetype: 2.3.0
whatwg-url: 8.7.0
data-view-buffer@1.0.2:
dependencies:
call-bound: 1.0.4
es-errors: 1.3.0
is-data-view: 1.0.2
data-view-byte-length@1.0.2:
dependencies:
call-bound: 1.0.4
es-errors: 1.3.0
is-data-view: 1.0.2
data-view-byte-offset@1.0.1:
dependencies:
call-bound: 1.0.4
es-errors: 1.3.0
is-data-view: 1.0.2
debug@2.6.9:
dependencies:
ms: 2.0.0
debug@3.2.7:
dependencies:
ms: 2.1.3
debug@4.4.0:
dependencies:
ms: 2.1.3
decamelize@1.2.0: {}
decimal.js@10.5.0: {}
dedent@0.7.0: {}
deep-equal@1.0.1: {}
deep-is@0.1.4: {}
deepmerge@4.3.1: {}
default-require-extensions@3.0.1:
dependencies:
strip-bom: 4.0.0
define-data-property@1.1.4:
dependencies:
es-define-property: 1.0.1
es-errors: 1.3.0
gopd: 1.2.0
define-properties@1.2.1:
dependencies:
define-data-property: 1.1.4
has-property-descriptors: 1.0.2
object-keys: 1.1.1
delayed-stream@1.0.0: {}
delegates@1.0.0: {}
depd@1.1.2: {}
depd@2.0.0: {}
destroy@1.2.0: {}
detect-indent@6.1.0: {}
detect-newline@3.1.0: {}
dezalgo@1.0.4:
dependencies:
asap: 2.0.6
wrappy: 1.0.2
diff-sequences@27.5.1: {}
dir-glob@3.0.1:
dependencies:
path-type: 4.0.0
doctrine@1.5.0:
dependencies:
esutils: 2.0.3
isarray: 1.0.0
doctrine@3.0.0:
dependencies:
esutils: 2.0.3
domexception@2.0.1:
dependencies:
webidl-conversions: 5.0.0
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.2
es-errors: 1.3.0
gopd: 1.2.0
ee-first@1.1.1: {}
electron-to-chromium@1.5.140: {}
emittery@0.8.1: {}
emoji-regex@7.0.3: {}
emoji-regex@8.0.0: {}
encodeurl@1.0.2: {}
encodeurl@2.0.0: {}
error-ex@1.3.2:
dependencies:
is-arrayish: 0.2.1
es-abstract@1.23.9:
dependencies:
array-buffer-byte-length: 1.0.2
arraybuffer.prototype.slice: 1.0.4
available-typed-arrays: 1.0.7
call-bind: 1.0.8
call-bound: 1.0.4
data-view-buffer: 1.0.2
data-view-byte-length: 1.0.2
data-view-byte-offset: 1.0.1
es-define-property: 1.0.1
es-errors: 1.3.0
es-object-atoms: 1.1.1
es-set-tostringtag: 2.1.0
es-to-primitive: 1.3.0
function.prototype.name: 1.1.8
get-intrinsic: 1.3.0
get-proto: 1.0.1
get-symbol-description: 1.1.0
globalthis: 1.0.4
gopd: 1.2.0
has-property-descriptors: 1.0.2
has-proto: 1.2.0
has-symbols: 1.1.0
hasown: 2.0.2
internal-slot: 1.1.0
is-array-buffer: 3.0.5
is-callable: 1.2.7
is-data-view: 1.0.2
is-regex: 1.2.1
is-shared-array-buffer: 1.0.4
is-string: 1.1.1
is-typed-array: 1.1.15
is-weakref: 1.1.1
math-intrinsics: 1.1.0
object-inspect: 1.13.4
object-keys: 1.1.1
object.assign: 4.1.7
own-keys: 1.0.1
regexp.prototype.flags: 1.5.4
safe-array-concat: 1.1.3
safe-push-apply: 1.0.0
safe-regex-test: 1.1.0
set-proto: 1.0.0
string.prototype.trim: 1.2.10
string.prototype.trimend: 1.0.9
string.prototype.trimstart: 1.0.8
typed-array-buffer: 1.0.3
typed-array-byte-length: 1.0.3
typed-array-byte-offset: 1.0.4
typed-array-length: 1.0.7
unbox-primitive: 1.1.0
which-typed-array: 1.1.19
es-define-property@1.0.1: {}
es-errors@1.3.0: {}
es-object-atoms@1.1.1:
dependencies:
es-errors: 1.3.0
es-set-tostringtag@2.1.0:
dependencies:
es-errors: 1.3.0
get-intrinsic: 1.3.0
has-tostringtag: 1.0.2
hasown: 2.0.2
es-shim-unscopables@1.1.0:
dependencies:
hasown: 2.0.2
es-to-primitive@1.3.0:
dependencies:
is-callable: 1.2.7
is-date-object: 1.1.0
is-symbol: 1.1.1
es6-error@4.1.1: {}
escalade@3.2.0: {}
escape-html@1.0.3: {}
escape-string-regexp@1.0.5: {}
escape-string-regexp@2.0.0: {}
escape-string-regexp@5.0.0: {}
escodegen@2.1.0:
dependencies:
esprima: 4.0.1
estraverse: 5.3.0
esutils: 2.0.3
optionalDependencies:
source-map: 0.6.1
eslint-config-airbnb-base@14.1.0(eslint-plugin-import@2.20.2(eslint@6.8.0))(eslint@6.8.0):
dependencies:
confusing-browser-globals: 1.0.11
eslint: 6.8.0
eslint-plugin-import: 2.20.2(eslint@6.8.0)
object.assign: 4.1.7
object.entries: 1.1.9
eslint-config-prettier@6.11.0(eslint@6.8.0):
dependencies:
eslint: 6.8.0
get-stdin: 6.0.0
eslint-import-resolver-node@0.3.9:
dependencies:
debug: 3.2.7
is-core-module: 2.16.1
resolve: 1.22.10
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.12.0(eslint-import-resolver-node@0.3.9)(eslint@6.8.0):
dependencies:
debug: 3.2.7
optionalDependencies:
eslint: 6.8.0
eslint-import-resolver-node: 0.3.9
transitivePeerDependencies:
- supports-color
eslint-plugin-import@2.20.2(eslint@6.8.0):
dependencies:
array-includes: 3.1.8
array.prototype.flat: 1.3.3
contains-path: 0.1.0
debug: 2.6.9
doctrine: 1.5.0
eslint: 6.8.0
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.0(eslint-import-resolver-node@0.3.9)(eslint@6.8.0)
has: 1.0.4
minimatch: 3.1.2
object.values: 1.2.1
read-pkg-up: 2.0.0
resolve: 1.22.10
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
eslint-plugin-prettier@3.1.3(eslint@6.8.0)(prettier@2.0.5):
dependencies:
eslint: 6.8.0
prettier: 2.0.5
prettier-linter-helpers: 1.0.0
eslint-scope@5.1.1:
dependencies:
esrecurse: 4.3.0
estraverse: 4.3.0
eslint-utils@1.4.3:
dependencies:
eslint-visitor-keys: 1.3.0
eslint-visitor-keys@1.3.0: {}
eslint@6.8.0:
dependencies:
'@babel/code-frame': 7.26.2
ajv: 6.12.6
chalk: 2.4.2
cross-spawn: 6.0.6
debug: 4.4.0
doctrine: 3.0.0
eslint-scope: 5.1.1
eslint-utils: 1.4.3
eslint-visitor-keys: 1.3.0
espree: 6.2.1
esquery: 1.6.0
esutils: 2.0.3
file-entry-cache: 5.0.1
functional-red-black-tree: 1.0.1
glob-parent: 5.1.2
globals: 12.4.0
ignore: 4.0.6
import-fresh: 3.3.1
imurmurhash: 0.1.4
inquirer: 7.3.3
is-glob: 4.0.3
js-yaml: 3.14.1
json-stable-stringify-without-jsonify: 1.0.1
levn: 0.3.0
lodash: 4.17.21
minimatch: 3.1.2
mkdirp: 0.5.6
natural-compare: 1.4.0
optionator: 0.8.3
progress: 2.0.3
regexpp: 2.0.1
semver: 6.3.1
strip-ansi: 5.2.0
strip-json-comments: 3.1.1
table: 5.4.6
text-table: 0.2.0
v8-compile-cache: 2.4.0
transitivePeerDependencies:
- supports-color
espree@6.2.1:
dependencies:
acorn: 7.4.1
acorn-jsx: 5.3.2(acorn@7.4.1)
eslint-visitor-keys: 1.3.0
esprima@4.0.1: {}
esquery@1.6.0:
dependencies:
estraverse: 5.3.0
esrecurse@4.3.0:
dependencies:
estraverse: 5.3.0
estraverse@4.3.0: {}
estraverse@5.3.0: {}
estree-walker@2.0.2: {}
esutils@2.0.3: {}
etag@1.8.1: {}
execa@5.1.1:
dependencies:
cross-spawn: 7.0.6
get-stream: 6.0.1
human-signals: 2.1.0
is-stream: 2.0.1
merge-stream: 2.0.0
npm-run-path: 4.0.1
onetime: 5.1.2
signal-exit: 3.0.7
strip-final-newline: 2.0.0
exit@0.1.2: {}
expect@27.5.1:
dependencies:
'@jest/types': 27.5.1
jest-get-type: 27.5.1
jest-matcher-utils: 27.5.1
jest-message-util: 27.5.1
express@4.21.2:
dependencies:
accepts: 1.3.8
array-flatten: 1.1.1
body-parser: 1.20.3
content-disposition: 0.5.4
content-type: 1.0.5
cookie: 0.7.1
cookie-signature: 1.0.6
debug: 2.6.9
depd: 2.0.0
encodeurl: 2.0.0
escape-html: 1.0.3
etag: 1.8.1
finalhandler: 1.3.1
fresh: 0.5.2
http-errors: 2.0.0
merge-descriptors: 1.0.3
methods: 1.1.2
on-finished: 2.4.1
parseurl: 1.3.3
path-to-regexp: 0.1.12
proxy-addr: 2.0.7
qs: 6.13.0
range-parser: 1.2.1
safe-buffer: 5.2.1
send: 0.19.0
serve-static: 1.16.2
setprototypeof: 1.2.0
statuses: 2.0.1
type-is: 1.6.18
utils-merge: 1.0.1
vary: 1.1.2
transitivePeerDependencies:
- supports-color
external-editor@3.1.0:
dependencies:
chardet: 0.7.0
iconv-lite: 0.4.24
tmp: 0.0.33
fast-deep-equal@3.1.3: {}
fast-diff@1.3.0: {}
fast-glob@3.3.3:
dependencies:
'@nodelib/fs.stat': 2.0.5
'@nodelib/fs.walk': 1.2.8
glob-parent: 5.1.2
merge2: 1.4.1
micromatch: 4.0.8
fast-json-stable-stringify@2.1.0: {}
fast-levenshtein@2.0.6: {}
fast-safe-stringify@2.1.1: {}
fastq@1.19.1:
dependencies:
reusify: 1.1.0
fb-watchman@2.0.2:
dependencies:
bser: 2.1.1
fetch-blob@3.2.0:
dependencies:
node-domexception: 1.0.0
web-streams-polyfill: 3.3.3
figures@3.2.0:
dependencies:
escape-string-regexp: 1.0.5
file-entry-cache@5.0.1:
dependencies:
flat-cache: 2.0.1
fill-range@7.1.1:
dependencies:
to-regex-range: 5.0.1
finalhandler@1.3.1:
dependencies:
debug: 2.6.9
encodeurl: 2.0.0
escape-html: 1.0.3
on-finished: 2.4.1
parseurl: 1.3.3
statuses: 2.0.1
unpipe: 1.0.0
transitivePeerDependencies:
- supports-color
find-cache-dir@3.3.2:
dependencies:
commondir: 1.0.1
make-dir: 3.1.0
pkg-dir: 4.2.0
find-up@2.1.0:
dependencies:
locate-path: 2.0.0
find-up@4.1.0:
dependencies:
locate-path: 5.0.0
path-exists: 4.0.0
flat-cache@2.0.1:
dependencies:
flatted: 2.0.2
rimraf: 2.6.3
write: 1.0.3
flatted@2.0.2: {}
for-each@0.3.5:
dependencies:
is-callable: 1.2.7
foreground-child@2.0.0:
dependencies:
cross-spawn: 7.0.6
signal-exit: 3.0.7
form-data@3.0.3:
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
es-set-tostringtag: 2.1.0
mime-types: 2.1.35
formdata-polyfill@4.0.10:
dependencies:
fetch-blob: 3.2.0
formidable@1.2.6: {}
forwarded@0.2.0: {}
fresh@0.5.2: {}
fromentries@1.3.2: {}
fs.realpath@1.0.0: {}
fsevents@2.3.3:
optional: true
function-bind@1.1.2: {}
function.prototype.name@1.1.8:
dependencies:
call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
functions-have-names: 1.2.3
hasown: 2.0.2
is-callable: 1.2.7
functional-red-black-tree@1.0.1: {}
functions-have-names@1.2.3: {}
gensync@1.0.0-beta.2: {}
get-caller-file@2.0.5: {}
get-intrinsic@1.3.0:
dependencies:
call-bind-apply-helpers: 1.0.2
es-define-property: 1.0.1
es-errors: 1.3.0
es-object-atoms: 1.1.1
function-bind: 1.1.2
get-proto: 1.0.1
gopd: 1.2.0
has-symbols: 1.1.0
hasown: 2.0.2
math-intrinsics: 1.1.0
get-package-type@0.1.0: {}
get-proto@1.0.1:
dependencies:
dunder-proto: 1.0.1
es-object-atoms: 1.1.1
get-stdin@6.0.0: {}
get-stream@6.0.1: {}
get-symbol-description@1.1.0:
dependencies:
call-bound: 1.0.4
es-errors: 1.3.0
get-intrinsic: 1.3.0
git-hooks-list@1.0.3: {}
glob-parent@5.1.2:
dependencies:
is-glob: 4.0.3
glob@7.2.3:
dependencies:
fs.realpath: 1.0.0
inflight: 1.0.6
inherits: 2.0.4
minimatch: 3.1.2
once: 1.4.0
path-is-absolute: 1.0.1
glob@8.1.0:
dependencies:
fs.realpath: 1.0.0
inflight: 1.0.6
inherits: 2.0.4
minimatch: 5.1.6
once: 1.4.0
globals@11.12.0: {}
globals@12.4.0:
dependencies:
type-fest: 0.8.1
globalthis@1.0.4:
dependencies:
define-properties: 1.2.1
gopd: 1.2.0
globby@10.0.0:
dependencies:
'@types/glob': 7.2.0
array-union: 2.1.0
dir-glob: 3.0.1
fast-glob: 3.3.3
glob: 7.2.3
ignore: 5.3.2
merge2: 1.4.1
slash: 3.0.0
gopd@1.2.0: {}
graceful-fs@4.2.11: {}
has-bigints@1.1.0: {}
has-flag@3.0.0: {}
has-flag@4.0.0: {}
has-property-descriptors@1.0.2:
dependencies:
es-define-property: 1.0.1
has-proto@1.2.0:
dependencies:
dunder-proto: 1.0.1
has-symbols@1.1.0: {}
has-tostringtag@1.0.2:
dependencies:
has-symbols: 1.1.0
has@1.0.4: {}
hasha@5.2.2:
dependencies:
is-stream: 2.0.1
type-fest: 0.8.1
hasown@2.0.2:
dependencies:
function-bind: 1.1.2
hosted-git-info@2.8.9: {}
html-encoding-sniffer@2.0.1:
dependencies:
whatwg-encoding: 1.0.5
html-escaper@2.0.2: {}
http-assert@1.5.0:
dependencies:
deep-equal: 1.0.1
http-errors: 1.8.1
http-errors@1.8.1:
dependencies:
depd: 1.1.2
inherits: 2.0.4
setprototypeof: 1.2.0
statuses: 1.5.0
toidentifier: 1.0.1
http-errors@2.0.0:
dependencies:
depd: 2.0.0
inherits: 2.0.4
setprototypeof: 1.2.0
statuses: 2.0.1
toidentifier: 1.0.1
http-proxy-agent@4.0.1:
dependencies:
'@tootallnate/once': 1.1.2
agent-base: 6.0.2
debug: 4.4.0
transitivePeerDependencies:
- supports-color
https-proxy-agent@5.0.1:
dependencies:
agent-base: 6.0.2
debug: 4.4.0
transitivePeerDependencies:
- supports-color
human-signals@2.1.0: {}
iconv-lite@0.4.24:
dependencies:
safer-buffer: 2.1.2
ignore@4.0.6: {}
ignore@5.3.2: {}
import-fresh@3.3.1:
dependencies:
parent-module: 1.0.1
resolve-from: 4.0.0
import-local@3.2.0:
dependencies:
pkg-dir: 4.2.0
resolve-cwd: 3.0.0
imurmurhash@0.1.4: {}
indent-string@4.0.0: {}
inflight@1.0.6:
dependencies:
once: 1.4.0
wrappy: 1.0.2
inherits@2.0.4: {}
inquirer@7.3.3:
dependencies:
ansi-escapes: 4.3.2
chalk: 4.1.2
cli-cursor: 3.1.0
cli-width: 3.0.0
external-editor: 3.1.0
figures: 3.2.0
lodash: 4.17.21
mute-stream: 0.0.8
run-async: 2.4.1
rxjs: 6.6.7
string-width: 4.2.3
strip-ansi: 6.0.1
through: 2.3.8
internal-slot@1.1.0:
dependencies:
es-errors: 1.3.0
hasown: 2.0.2
side-channel: 1.1.0
ipaddr.js@1.9.1: {}
is-array-buffer@3.0.5:
dependencies:
call-bind: 1.0.8
call-bound: 1.0.4
get-intrinsic: 1.3.0
is-arrayish@0.2.1: {}
is-async-function@2.1.1:
dependencies:
async-function: 1.0.0
call-bound: 1.0.4
get-proto: 1.0.1
has-tostringtag: 1.0.2
safe-regex-test: 1.1.0
is-bigint@1.1.0:
dependencies:
has-bigints: 1.1.0
is-boolean-object@1.2.2:
dependencies:
call-bound: 1.0.4
has-tostringtag: 1.0.2
is-callable@1.2.7: {}
is-core-module@2.16.1:
dependencies:
hasown: 2.0.2
is-data-view@1.0.2:
dependencies:
call-bound: 1.0.4
get-intrinsic: 1.3.0
is-typed-array: 1.1.15
is-date-object@1.1.0:
dependencies:
call-bound: 1.0.4
has-tostringtag: 1.0.2
is-extglob@2.1.1: {}
is-finalizationregistry@1.1.1:
dependencies:
call-bound: 1.0.4
is-fullwidth-code-point@2.0.0: {}
is-fullwidth-code-point@3.0.0: {}
is-generator-fn@2.1.0: {}
is-generator-function@1.1.0:
dependencies:
call-bound: 1.0.4
get-proto: 1.0.1
has-tostringtag: 1.0.2
safe-regex-test: 1.1.0
is-glob@4.0.3:
dependencies:
is-extglob: 2.1.1
is-map@2.0.3: {}
is-module@1.0.0: {}
is-number-object@1.1.1:
dependencies:
call-bound: 1.0.4
has-tostringtag: 1.0.2
is-number@7.0.0: {}
is-plain-obj@2.1.0: {}
is-potential-custom-element-name@1.0.1: {}
is-reference@1.2.1:
dependencies:
'@types/estree': 1.0.7
is-regex@1.2.1:
dependencies:
call-bound: 1.0.4
gopd: 1.2.0
has-tostringtag: 1.0.2
hasown: 2.0.2
is-set@2.0.3: {}
is-shared-array-buffer@1.0.4:
dependencies:
call-bound: 1.0.4
is-stream@2.0.1: {}
is-string@1.1.1:
dependencies:
call-bound: 1.0.4
has-tostringtag: 1.0.2
is-symbol@1.1.1:
dependencies:
call-bound: 1.0.4
has-symbols: 1.1.0
safe-regex-test: 1.1.0
is-typed-array@1.1.15:
dependencies:
which-typed-array: 1.1.19
is-typedarray@1.0.0: {}
is-weakmap@2.0.2: {}
is-weakref@1.1.1:
dependencies:
call-bound: 1.0.4
is-weakset@2.0.4:
dependencies:
call-bound: 1.0.4
get-intrinsic: 1.3.0
is-windows@1.0.2: {}
isarray@1.0.0: {}
isarray@2.0.5: {}
isexe@2.0.0: {}
istanbul-lib-coverage@3.2.2: {}
istanbul-lib-hook@3.0.0:
dependencies:
append-transform: 2.0.0
istanbul-lib-instrument@4.0.3:
dependencies:
'@babel/core': 7.26.10
'@istanbuljs/schema': 0.1.3
istanbul-lib-coverage: 3.2.2
semver: 6.3.1
transitivePeerDependencies:
- supports-color
istanbul-lib-instrument@5.2.1:
dependencies:
'@babel/core': 7.26.10
'@babel/parser': 7.27.0
'@istanbuljs/schema': 0.1.3
istanbul-lib-coverage: 3.2.2
semver: 6.3.1
transitivePeerDependencies:
- supports-color
istanbul-lib-processinfo@2.0.3:
dependencies:
archy: 1.0.0
cross-spawn: 7.0.6
istanbul-lib-coverage: 3.2.2
p-map: 3.0.0
rimraf: 3.0.2
uuid: 8.3.2
istanbul-lib-report@3.0.1:
dependencies:
istanbul-lib-coverage: 3.2.2
make-dir: 4.0.0
supports-color: 7.2.0
istanbul-lib-source-maps@4.0.1:
dependencies:
debug: 4.4.0
istanbul-lib-coverage: 3.2.2
source-map: 0.6.1
transitivePeerDependencies:
- supports-color
istanbul-reports@3.1.7:
dependencies:
html-escaper: 2.0.2
istanbul-lib-report: 3.0.1
jest-changed-files@27.5.1:
dependencies:
'@jest/types': 27.5.1
execa: 5.1.1
throat: 6.0.2
jest-circus@27.5.1:
dependencies:
'@jest/environment': 27.5.1
'@jest/test-result': 27.5.1
'@jest/types': 27.5.1
'@types/node': 22.14.1
chalk: 4.1.2
co: 4.6.0
dedent: 0.7.0
expect: 27.5.1
is-generator-fn: 2.1.0
jest-each: 27.5.1
jest-matcher-utils: 27.5.1
jest-message-util: 27.5.1
jest-runtime: 27.5.1
jest-snapshot: 27.5.1
jest-util: 27.5.1
pretty-format: 27.5.1
slash: 3.0.0
stack-utils: 2.0.6
throat: 6.0.2
transitivePeerDependencies:
- supports-color
jest-cli@27.5.1:
dependencies:
'@jest/core': 27.5.1
'@jest/test-result': 27.5.1
'@jest/types': 27.5.1
chalk: 4.1.2
exit: 0.1.2
graceful-fs: 4.2.11
import-local: 3.2.0
jest-config: 27.5.1
jest-util: 27.5.1
jest-validate: 27.5.1
prompts: 2.4.2
yargs: 16.2.0
transitivePeerDependencies:
- bufferutil
- canvas
- supports-color
- ts-node
- utf-8-validate
jest-config@27.5.1:
dependencies:
'@babel/core': 7.26.10
'@jest/test-sequencer': 27.5.1
'@jest/types': 27.5.1
babel-jest: 27.5.1(@babel/core@7.26.10)
chalk: 4.1.2
ci-info: 3.9.0
deepmerge: 4.3.1
glob: 7.2.3
graceful-fs: 4.2.11
jest-circus: 27.5.1
jest-environment-jsdom: 27.5.1
jest-environment-node: 27.5.1
jest-get-type: 27.5.1
jest-jasmine2: 27.5.1
jest-regex-util: 27.5.1
jest-resolve: 27.5.1
jest-runner: 27.5.1
jest-util: 27.5.1
jest-validate: 27.5.1
micromatch: 4.0.8
parse-json: 5.2.0
pretty-format: 27.5.1
slash: 3.0.0
strip-json-comments: 3.1.1
transitivePeerDependencies:
- bufferutil
- canvas
- supports-color
- utf-8-validate
jest-diff@27.5.1:
dependencies:
chalk: 4.1.2
diff-sequences: 27.5.1
jest-get-type: 27.5.1
pretty-format: 27.5.1
jest-docblock@27.5.1:
dependencies:
detect-newline: 3.1.0
jest-each@27.5.1:
dependencies:
'@jest/types': 27.5.1
chalk: 4.1.2
jest-get-type: 27.5.1
jest-util: 27.5.1
pretty-format: 27.5.1
jest-environment-jsdom@27.5.1:
dependencies:
'@jest/environment': 27.5.1
'@jest/fake-timers': 27.5.1
'@jest/types': 27.5.1
'@types/node': 22.14.1
jest-mock: 27.5.1
jest-util: 27.5.1
jsdom: 16.7.0
transitivePeerDependencies:
- bufferutil
- canvas
- supports-color
- utf-8-validate
jest-environment-node@27.5.1:
dependencies:
'@jest/environment': 27.5.1
'@jest/fake-timers': 27.5.1
'@jest/types': 27.5.1
'@types/node': 22.14.1
jest-mock: 27.5.1
jest-util: 27.5.1
jest-get-type@27.5.1: {}
jest-haste-map@27.5.1:
dependencies:
'@jest/types': 27.5.1
'@types/graceful-fs': 4.1.9
'@types/node': 22.14.1
anymatch: 3.1.3
fb-watchman: 2.0.2
graceful-fs: 4.2.11
jest-regex-util: 27.5.1
jest-serializer: 27.5.1
jest-util: 27.5.1
jest-worker: 27.5.1
micromatch: 4.0.8
walker: 1.0.8
optionalDependencies:
fsevents: 2.3.3
jest-jasmine2@27.5.1:
dependencies:
'@jest/environment': 27.5.1
'@jest/source-map': 27.5.1
'@jest/test-result': 27.5.1
'@jest/types': 27.5.1
'@types/node': 22.14.1
chalk: 4.1.2
co: 4.6.0
expect: 27.5.1
is-generator-fn: 2.1.0
jest-each: 27.5.1
jest-matcher-utils: 27.5.1
jest-message-util: 27.5.1
jest-runtime: 27.5.1
jest-snapshot: 27.5.1
jest-util: 27.5.1
pretty-format: 27.5.1
throat: 6.0.2
transitivePeerDependencies:
- supports-color
jest-leak-detector@27.5.1:
dependencies:
jest-get-type: 27.5.1
pretty-format: 27.5.1
jest-matcher-utils@27.5.1:
dependencies:
chalk: 4.1.2
jest-diff: 27.5.1
jest-get-type: 27.5.1
pretty-format: 27.5.1
jest-message-util@27.5.1:
dependencies:
'@babel/code-frame': 7.26.2
'@jest/types': 27.5.1
'@types/stack-utils': 2.0.3
chalk: 4.1.2
graceful-fs: 4.2.11
micromatch: 4.0.8
pretty-format: 27.5.1
slash: 3.0.0
stack-utils: 2.0.6
jest-mock@27.5.1:
dependencies:
'@jest/types': 27.5.1
'@types/node': 22.14.1
jest-pnp-resolver@1.2.3(jest-resolve@27.5.1):
optionalDependencies:
jest-resolve: 27.5.1
jest-regex-util@27.5.1: {}
jest-resolve-dependencies@27.5.1:
dependencies:
'@jest/types': 27.5.1
jest-regex-util: 27.5.1
jest-snapshot: 27.5.1
transitivePeerDependencies:
- supports-color
jest-resolve@27.5.1:
dependencies:
'@jest/types': 27.5.1
chalk: 4.1.2
graceful-fs: 4.2.11
jest-haste-map: 27.5.1
jest-pnp-resolver: 1.2.3(jest-resolve@27.5.1)
jest-util: 27.5.1
jest-validate: 27.5.1
resolve: 1.22.10
resolve.exports: 1.1.1
slash: 3.0.0
jest-runner@27.5.1:
dependencies:
'@jest/console': 27.5.1
'@jest/environment': 27.5.1
'@jest/test-result': 27.5.1
'@jest/transform': 27.5.1
'@jest/types': 27.5.1
'@types/node': 22.14.1
chalk: 4.1.2
emittery: 0.8.1
graceful-fs: 4.2.11
jest-docblock: 27.5.1
jest-environment-jsdom: 27.5.1
jest-environment-node: 27.5.1
jest-haste-map: 27.5.1
jest-leak-detector: 27.5.1
jest-message-util: 27.5.1
jest-resolve: 27.5.1
jest-runtime: 27.5.1
jest-util: 27.5.1
jest-worker: 27.5.1
source-map-support: 0.5.21
throat: 6.0.2
transitivePeerDependencies:
- bufferutil
- canvas
- supports-color
- utf-8-validate
jest-runtime@27.5.1:
dependencies:
'@jest/environment': 27.5.1
'@jest/fake-timers': 27.5.1
'@jest/globals': 27.5.1
'@jest/source-map': 27.5.1
'@jest/test-result': 27.5.1
'@jest/transform': 27.5.1
'@jest/types': 27.5.1
chalk: 4.1.2
cjs-module-lexer: 1.4.3
collect-v8-coverage: 1.0.2
execa: 5.1.1
glob: 7.2.3
graceful-fs: 4.2.11
jest-haste-map: 27.5.1
jest-message-util: 27.5.1
jest-mock: 27.5.1
jest-regex-util: 27.5.1
jest-resolve: 27.5.1
jest-snapshot: 27.5.1
jest-util: 27.5.1
slash: 3.0.0
strip-bom: 4.0.0
transitivePeerDependencies:
- supports-color
jest-serializer@27.5.1:
dependencies:
'@types/node': 22.14.1
graceful-fs: 4.2.11
jest-snapshot@27.5.1:
dependencies:
'@babel/core': 7.26.10
'@babel/generator': 7.27.0
'@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.10)
'@babel/traverse': 7.27.0
'@babel/types': 7.27.0
'@jest/transform': 27.5.1
'@jest/types': 27.5.1
'@types/babel__traverse': 7.20.7
'@types/prettier': 2.7.3
babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.10)
chalk: 4.1.2
expect: 27.5.1
graceful-fs: 4.2.11
jest-diff: 27.5.1
jest-get-type: 27.5.1
jest-haste-map: 27.5.1
jest-matcher-utils: 27.5.1
jest-message-util: 27.5.1
jest-util: 27.5.1
natural-compare: 1.4.0
pretty-format: 27.5.1
semver: 7.7.1
transitivePeerDependencies:
- supports-color
jest-util@27.5.1:
dependencies:
'@jest/types': 27.5.1
'@types/node': 22.14.1
chalk: 4.1.2
ci-info: 3.9.0
graceful-fs: 4.2.11
picomatch: 2.3.1
jest-validate@27.5.1:
dependencies:
'@jest/types': 27.5.1
camelcase: 6.3.0
chalk: 4.1.2
jest-get-type: 27.5.1
leven: 3.1.0
pretty-format: 27.5.1
jest-watcher@27.5.1:
dependencies:
'@jest/test-result': 27.5.1
'@jest/types': 27.5.1
'@types/node': 22.14.1
ansi-escapes: 4.3.2
chalk: 4.1.2
jest-util: 27.5.1
string-length: 4.0.2
jest-worker@27.5.1:
dependencies:
'@types/node': 22.14.1
merge-stream: 2.0.0
supports-color: 8.1.1
jest@27.2.4:
dependencies:
'@jest/core': 27.5.1
import-local: 3.2.0
jest-cli: 27.5.1
transitivePeerDependencies:
- bufferutil
- canvas
- supports-color
- ts-node
- utf-8-validate
js-tokens@4.0.0: {}
js-yaml@3.14.1:
dependencies:
argparse: 1.0.10
esprima: 4.0.1
jsdom@16.7.0:
dependencies:
abab: 2.0.6
acorn: 8.14.1
acorn-globals: 6.0.0
cssom: 0.4.4
cssstyle: 2.3.0
data-urls: 2.0.0
decimal.js: 10.5.0
domexception: 2.0.1
escodegen: 2.1.0
form-data: 3.0.3
html-encoding-sniffer: 2.0.1
http-proxy-agent: 4.0.1
https-proxy-agent: 5.0.1
is-potential-custom-element-name: 1.0.1
nwsapi: 2.2.20
parse5: 6.0.1
saxes: 5.0.1
symbol-tree: 3.2.4
tough-cookie: 4.1.4
w3c-hr-time: 1.0.2
w3c-xmlserializer: 2.0.0
webidl-conversions: 6.1.0
whatwg-encoding: 1.0.5
whatwg-mimetype: 2.3.0
whatwg-url: 8.7.0
ws: 7.5.10
xml-name-validator: 3.0.0
transitivePeerDependencies:
- bufferutil
- supports-color
- utf-8-validate
jsesc@3.1.0: {}
json-parse-even-better-errors@2.3.1: {}
json-schema-traverse@0.4.1: {}
json-stable-stringify-without-jsonify@1.0.1: {}
json5@2.2.3: {}
keygrip@1.1.0:
dependencies:
tsscmp: 1.0.6
kleur@3.0.3: {}
koa-compose@4.1.0: {}
koa-convert@2.0.0:
dependencies:
co: 4.6.0
koa-compose: 4.1.0
koa@2.16.1:
dependencies:
accepts: 1.3.8
cache-content-type: 1.0.1
content-disposition: 0.5.4
content-type: 1.0.5
cookies: 0.9.1
debug: 4.4.0
delegates: 1.0.0
depd: 2.0.0
destroy: 1.2.0
encodeurl: 1.0.2
escape-html: 1.0.3
fresh: 0.5.2
http-assert: 1.5.0
http-errors: 1.8.1
is-generator-function: 1.1.0
koa-compose: 4.1.0
koa-convert: 2.0.0
on-finished: 2.4.1
only: 0.0.2
parseurl: 1.3.3
statuses: 1.5.0
type-is: 1.6.18
vary: 1.1.2
transitivePeerDependencies:
- supports-color
leven@3.1.0: {}
levn@0.3.0:
dependencies:
prelude-ls: 1.1.2
type-check: 0.3.2
lines-and-columns@1.2.4: {}
load-json-file@2.0.0:
dependencies:
graceful-fs: 4.2.11
parse-json: 2.2.0
pify: 2.3.0
strip-bom: 3.0.0
locate-path@2.0.0:
dependencies:
p-locate: 2.0.0
path-exists: 3.0.0
locate-path@5.0.0:
dependencies:
p-locate: 4.1.0
lodash.flattendeep@4.4.0: {}
lodash@4.17.21: {}
lru-cache@5.1.1:
dependencies:
yallist: 3.1.1
magic-string@0.30.17:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0
make-dir@3.1.0:
dependencies:
semver: 6.3.1
make-dir@4.0.0:
dependencies:
semver: 7.7.1
makeerror@1.0.12:
dependencies:
tmpl: 1.0.5
math-intrinsics@1.1.0: {}
media-typer@0.3.0: {}
merge-descriptors@1.0.3: {}
merge-stream@2.0.0: {}
merge2@1.4.1: {}
methods@1.1.2: {}
micromatch@4.0.8:
dependencies:
braces: 3.0.3
picomatch: 2.3.1
mime-db@1.52.0: {}
mime-types@2.1.35:
dependencies:
mime-db: 1.52.0
mime@1.6.0: {}
mime@2.6.0: {}
mimic-fn@2.1.0: {}
minimatch@3.1.2:
dependencies:
brace-expansion: 1.1.11
minimatch@5.1.6:
dependencies:
brace-expansion: 2.0.1
minimist@1.2.8: {}
mkdirp@0.5.6:
dependencies:
minimist: 1.2.8
ms@2.0.0: {}
ms@2.1.3: {}
mute-stream@0.0.8: {}
natural-compare@1.4.0: {}
negotiator@0.6.3: {}
nice-try@1.0.5: {}
node-domexception@1.0.0: {}
node-int64@0.4.0: {}
node-preload@0.2.1:
dependencies:
process-on-spawn: 1.1.0
node-releases@2.0.19: {}
normalize-package-data@2.5.0:
dependencies:
hosted-git-info: 2.8.9
resolve: 1.22.10
semver: 5.7.2
validate-npm-package-license: 3.0.4
normalize-path@3.0.0: {}
npm-run-path@4.0.1:
dependencies:
path-key: 3.1.1
nwsapi@2.2.20: {}
nyc@15.1.0:
dependencies:
'@istanbuljs/load-nyc-config': 1.1.0
'@istanbuljs/schema': 0.1.3
caching-transform: 4.0.0
convert-source-map: 1.9.0
decamelize: 1.2.0
find-cache-dir: 3.3.2
find-up: 4.1.0
foreground-child: 2.0.0
get-package-type: 0.1.0
glob: 7.2.3
istanbul-lib-coverage: 3.2.2
istanbul-lib-hook: 3.0.0
istanbul-lib-instrument: 4.0.3
istanbul-lib-processinfo: 2.0.3
istanbul-lib-report: 3.0.1
istanbul-lib-source-maps: 4.0.1
istanbul-reports: 3.1.7
make-dir: 3.1.0
node-preload: 0.2.1
p-map: 3.0.0
process-on-spawn: 1.1.0
resolve-from: 5.0.0
rimraf: 3.0.2
signal-exit: 3.0.7
spawn-wrap: 2.0.0
test-exclude: 6.0.0
yargs: 15.4.1
transitivePeerDependencies:
- supports-color
object-inspect@1.13.4: {}
object-keys@1.1.1: {}
object.assign@4.1.7:
dependencies:
call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
es-object-atoms: 1.1.1
has-symbols: 1.1.0
object-keys: 1.1.1
object.entries@1.1.9:
dependencies:
call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
es-object-atoms: 1.1.1
object.values@1.2.1:
dependencies:
call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
es-object-atoms: 1.1.1
on-finished@2.4.1:
dependencies:
ee-first: 1.1.1
once@1.4.0:
dependencies:
wrappy: 1.0.2
onetime@5.1.2:
dependencies:
mimic-fn: 2.1.0
only@0.0.2: {}
optionator@0.8.3:
dependencies:
deep-is: 0.1.4
fast-levenshtein: 2.0.6
levn: 0.3.0
prelude-ls: 1.1.2
type-check: 0.3.2
word-wrap: 1.2.5
os-tmpdir@1.0.2: {}
own-keys@1.0.1:
dependencies:
get-intrinsic: 1.3.0
object-keys: 1.1.1
safe-push-apply: 1.0.0
p-limit@1.3.0:
dependencies:
p-try: 1.0.0
p-limit@2.3.0:
dependencies:
p-try: 2.2.0
p-locate@2.0.0:
dependencies:
p-limit: 1.3.0
p-locate@4.1.0:
dependencies:
p-limit: 2.3.0
p-map@3.0.0:
dependencies:
aggregate-error: 3.1.0
p-try@1.0.0: {}
p-try@2.2.0: {}
package-hash@4.0.0:
dependencies:
graceful-fs: 4.2.11
hasha: 5.2.2
lodash.flattendeep: 4.4.0
release-zalgo: 1.0.0
parent-module@1.0.1:
dependencies:
callsites: 3.1.0
parse-json@2.2.0:
dependencies:
error-ex: 1.3.2
parse-json@5.2.0:
dependencies:
'@babel/code-frame': 7.26.2
error-ex: 1.3.2
json-parse-even-better-errors: 2.3.1
lines-and-columns: 1.2.4
parse5@6.0.1: {}
parseurl@1.3.3: {}
path-exists@3.0.0: {}
path-exists@4.0.0: {}
path-is-absolute@1.0.1: {}
path-key@2.0.1: {}
path-key@3.1.1: {}
path-parse@1.0.7: {}
path-to-regexp@0.1.12: {}
path-type@2.0.0:
dependencies:
pify: 2.3.0
path-type@4.0.0: {}
picocolors@1.1.1: {}
picomatch@2.3.1: {}
picomatch@4.0.2: {}
pify@2.3.0: {}
pirates@4.0.7: {}
pkg-dir@4.2.0:
dependencies:
find-up: 4.1.0
possible-typed-array-names@1.1.0: {}
prelude-ls@1.1.2: {}
prettier-linter-helpers@1.0.0:
dependencies:
fast-diff: 1.3.0
prettier-plugin-pkgjson@0.2.8(prettier@2.0.5):
dependencies:
prettier: 2.0.5
sort-package-json: 1.57.0
prettier@2.0.5: {}
pretty-format@27.5.1:
dependencies:
ansi-regex: 5.0.1
ansi-styles: 5.2.0
react-is: 17.0.2
process-on-spawn@1.1.0:
dependencies:
fromentries: 1.3.2
progress@2.0.3: {}
prompts@2.4.2:
dependencies:
kleur: 3.0.3
sisteransi: 1.0.5
proxy-addr@2.0.7:
dependencies:
forwarded: 0.2.0
ipaddr.js: 1.9.1
psl@1.15.0:
dependencies:
punycode: 2.3.1
punycode@2.3.1: {}
qs@6.13.0:
dependencies:
side-channel: 1.1.0
qs@6.14.0:
dependencies:
side-channel: 1.1.0
querystringify@2.2.0: {}
queue-microtask@1.2.3: {}
range-parser@1.2.1: {}
raw-body@2.5.2:
dependencies:
bytes: 3.1.2
http-errors: 2.0.0
iconv-lite: 0.4.24
unpipe: 1.0.0
react-is@17.0.2: {}
read-pkg-up@2.0.0:
dependencies:
find-up: 2.1.0
read-pkg: 2.0.0
read-pkg@2.0.0:
dependencies:
load-json-file: 2.0.0
normalize-package-data: 2.5.0
path-type: 2.0.0
readable-stream@3.6.2:
dependencies:
inherits: 2.0.4
string_decoder: 1.3.0
util-deprecate: 1.0.2
reflect.getprototypeof@1.0.10:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
es-abstract: 1.23.9
es-errors: 1.3.0
es-object-atoms: 1.1.1
get-intrinsic: 1.3.0
get-proto: 1.0.1
which-builtin-type: 1.2.1
regexp.prototype.flags@1.5.4:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
es-errors: 1.3.0
get-proto: 1.0.1
gopd: 1.2.0
set-function-name: 2.0.2
regexpp@2.0.1: {}
release-zalgo@1.0.0:
dependencies:
es6-error: 4.1.1
require-directory@2.1.1: {}
require-main-filename@2.0.0: {}
requires-port@1.0.0: {}
resolve-cwd@3.0.0:
dependencies:
resolve-from: 5.0.0
resolve-from@4.0.0: {}
resolve-from@5.0.0: {}
resolve.exports@1.1.1: {}
resolve@1.22.10:
dependencies:
is-core-module: 2.16.1
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
restore-cursor@3.1.0:
dependencies:
onetime: 5.1.2
signal-exit: 3.0.7
reusify@1.1.0: {}
rimraf@2.6.3:
dependencies:
glob: 7.2.3
rimraf@3.0.2:
dependencies:
glob: 7.2.3
rollup@3.29.5:
optionalDependencies:
fsevents: 2.3.3
run-async@2.4.1: {}
run-parallel@1.2.0:
dependencies:
queue-microtask: 1.2.3
rxjs@6.6.7:
dependencies:
tslib: 1.14.1
safe-array-concat@1.1.3:
dependencies:
call-bind: 1.0.8
call-bound: 1.0.4
get-intrinsic: 1.3.0
has-symbols: 1.1.0
isarray: 2.0.5
safe-buffer@5.2.1: {}
safe-push-apply@1.0.0:
dependencies:
es-errors: 1.3.0
isarray: 2.0.5
safe-regex-test@1.1.0:
dependencies:
call-bound: 1.0.4
es-errors: 1.3.0
is-regex: 1.2.1
safer-buffer@2.1.2: {}
saxes@5.0.1:
dependencies:
xmlchars: 2.2.0
semver@5.7.2: {}
semver@6.3.1: {}
semver@7.7.1: {}
send@0.19.0:
dependencies:
debug: 2.6.9
depd: 2.0.0
destroy: 1.2.0
encodeurl: 1.0.2
escape-html: 1.0.3
etag: 1.8.1
fresh: 0.5.2
http-errors: 2.0.0
mime: 1.6.0
ms: 2.1.3
on-finished: 2.4.1
range-parser: 1.2.1
statuses: 2.0.1
transitivePeerDependencies:
- supports-color
serve-static@1.16.2:
dependencies:
encodeurl: 2.0.0
escape-html: 1.0.3
parseurl: 1.3.3
send: 0.19.0
transitivePeerDependencies:
- supports-color
set-blocking@2.0.0: {}
set-function-length@1.2.2:
dependencies:
define-data-property: 1.1.4
es-errors: 1.3.0
function-bind: 1.1.2
get-intrinsic: 1.3.0
gopd: 1.2.0
has-property-descriptors: 1.0.2
set-function-name@2.0.2:
dependencies:
define-data-property: 1.1.4
es-errors: 1.3.0
functions-have-names: 1.2.3
has-property-descriptors: 1.0.2
set-proto@1.0.0:
dependencies:
dunder-proto: 1.0.1
es-errors: 1.3.0
es-object-atoms: 1.1.1
setprototypeof@1.2.0: {}
shebang-command@1.2.0:
dependencies:
shebang-regex: 1.0.0
shebang-command@2.0.0:
dependencies:
shebang-regex: 3.0.0
shebang-regex@1.0.0: {}
shebang-regex@3.0.0: {}
side-channel-list@1.0.0:
dependencies:
es-errors: 1.3.0
object-inspect: 1.13.4
side-channel-map@1.0.1:
dependencies:
call-bound: 1.0.4
es-errors: 1.3.0
get-intrinsic: 1.3.0
object-inspect: 1.13.4
side-channel-weakmap@1.0.2:
dependencies:
call-bound: 1.0.4
es-errors: 1.3.0
get-intrinsic: 1.3.0
object-inspect: 1.13.4
side-channel-map: 1.0.1
side-channel@1.1.0:
dependencies:
es-errors: 1.3.0
object-inspect: 1.13.4
side-channel-list: 1.0.0
side-channel-map: 1.0.1
side-channel-weakmap: 1.0.2
signal-exit@3.0.7: {}
sisteransi@1.0.5: {}
slash@3.0.0: {}
slice-ansi@2.1.0:
dependencies:
ansi-styles: 3.2.1
astral-regex: 1.0.0
is-fullwidth-code-point: 2.0.0
sort-object-keys@1.1.3: {}
sort-package-json@1.57.0:
dependencies:
detect-indent: 6.1.0
detect-newline: 3.1.0
git-hooks-list: 1.0.3
globby: 10.0.0
is-plain-obj: 2.1.0
sort-object-keys: 1.1.3
source-map-support@0.5.21:
dependencies:
buffer-from: 1.1.2
source-map: 0.6.1
source-map@0.6.1: {}
source-map@0.7.4: {}
spawn-wrap@2.0.0:
dependencies:
foreground-child: 2.0.0
is-windows: 1.0.2
make-dir: 3.1.0
rimraf: 3.0.2
signal-exit: 3.0.7
which: 2.0.2
spdx-correct@3.2.0:
dependencies:
spdx-expression-parse: 3.0.1
spdx-license-ids: 3.0.21
spdx-exceptions@2.5.0: {}
spdx-expression-parse@3.0.1:
dependencies:
spdx-exceptions: 2.5.0
spdx-license-ids: 3.0.21
spdx-license-ids@3.0.21: {}
sprintf-js@1.0.3: {}
stack-utils@2.0.6:
dependencies:
escape-string-regexp: 2.0.0
statuses@1.5.0: {}
statuses@2.0.1: {}
string-length@4.0.2:
dependencies:
char-regex: 1.0.2
strip-ansi: 6.0.1
string-width@3.1.0:
dependencies:
emoji-regex: 7.0.3
is-fullwidth-code-point: 2.0.0
strip-ansi: 5.2.0
string-width@4.2.3:
dependencies:
emoji-regex: 8.0.0
is-fullwidth-code-point: 3.0.0
strip-ansi: 6.0.1
string.prototype.trim@1.2.10:
dependencies:
call-bind: 1.0.8
call-bound: 1.0.4
define-data-property: 1.1.4
define-properties: 1.2.1
es-abstract: 1.23.9
es-object-atoms: 1.1.1
has-property-descriptors: 1.0.2
string.prototype.trimend@1.0.9:
dependencies:
call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
es-object-atoms: 1.1.1
string.prototype.trimstart@1.0.8:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
es-object-atoms: 1.1.1
string_decoder@1.3.0:
dependencies:
safe-buffer: 5.2.1
strip-ansi@5.2.0:
dependencies:
ansi-regex: 4.1.1
strip-ansi@6.0.1:
dependencies:
ansi-regex: 5.0.1
strip-bom@3.0.0: {}
strip-bom@4.0.0: {}
strip-final-newline@2.0.0: {}
strip-json-comments@3.1.1: {}
superagent@6.1.0:
dependencies:
component-emitter: 1.3.1
cookiejar: 2.1.4
debug: 4.4.0
fast-safe-stringify: 2.1.1
form-data: 3.0.3
formidable: 1.2.6
methods: 1.1.2
mime: 2.6.0
qs: 6.14.0
readable-stream: 3.6.2
semver: 7.7.1
transitivePeerDependencies:
- supports-color
supertest@6.1.6:
dependencies:
methods: 1.1.2
superagent: 6.1.0
transitivePeerDependencies:
- supports-color
supports-color@5.5.0:
dependencies:
has-flag: 3.0.0
supports-color@7.2.0:
dependencies:
has-flag: 4.0.0
supports-color@8.1.1:
dependencies:
has-flag: 4.0.0
supports-hyperlinks@2.3.0:
dependencies:
has-flag: 4.0.0
supports-color: 7.2.0
supports-preserve-symlinks-flag@1.0.0: {}
symbol-tree@3.2.4: {}
table@5.4.6:
dependencies:
ajv: 6.12.6
lodash: 4.17.21
slice-ansi: 2.1.0
string-width: 3.1.0
terminal-link@2.1.1:
dependencies:
ansi-escapes: 4.3.2
supports-hyperlinks: 2.3.0
test-exclude@6.0.0:
dependencies:
'@istanbuljs/schema': 0.1.3
glob: 7.2.3
minimatch: 3.1.2
text-table@0.2.0: {}
throat@6.0.2: {}
through@2.3.8: {}
tmp@0.0.33:
dependencies:
os-tmpdir: 1.0.2
tmpl@1.0.5: {}
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0
toidentifier@1.0.1: {}
tough-cookie@4.1.4:
dependencies:
psl: 1.15.0
punycode: 2.3.1
universalify: 0.2.0
url-parse: 1.5.10
tr46@2.1.0:
dependencies:
punycode: 2.3.1
tslib@1.14.1: {}
tsscmp@1.0.6: {}
type-check@0.3.2:
dependencies:
prelude-ls: 1.1.2
type-detect@4.0.8: {}
type-fest@0.21.3: {}
type-fest@0.8.1: {}
type-is@1.6.18:
dependencies:
media-typer: 0.3.0
mime-types: 2.1.35
typed-array-buffer@1.0.3:
dependencies:
call-bound: 1.0.4
es-errors: 1.3.0
is-typed-array: 1.1.15
typed-array-byte-length@1.0.3:
dependencies:
call-bind: 1.0.8
for-each: 0.3.5
gopd: 1.2.0
has-proto: 1.2.0
is-typed-array: 1.1.15
typed-array-byte-offset@1.0.4:
dependencies:
available-typed-arrays: 1.0.7
call-bind: 1.0.8
for-each: 0.3.5
gopd: 1.2.0
has-proto: 1.2.0
is-typed-array: 1.1.15
reflect.getprototypeof: 1.0.10
typed-array-length@1.0.7:
dependencies:
call-bind: 1.0.8
for-each: 0.3.5
gopd: 1.2.0
is-typed-array: 1.1.15
possible-typed-array-names: 1.1.0
reflect.getprototypeof: 1.0.10
typedarray-to-buffer@3.1.5:
dependencies:
is-typedarray: 1.0.0
unbox-primitive@1.1.0:
dependencies:
call-bound: 1.0.4
has-bigints: 1.1.0
has-symbols: 1.1.0
which-boxed-primitive: 1.1.1
undici-types@6.21.0: {}
universalify@0.2.0: {}
unpipe@1.0.0: {}
update-browserslist-db@1.1.3(browserslist@4.24.4):
dependencies:
browserslist: 4.24.4
escalade: 3.2.0
picocolors: 1.1.1
uri-js@4.4.1:
dependencies:
punycode: 2.3.1
url-parse@1.5.10:
dependencies:
querystringify: 2.2.0
requires-port: 1.0.0
util-deprecate@1.0.2: {}
utils-merge@1.0.1: {}
uuid@8.3.2: {}
v8-compile-cache@2.4.0: {}
v8-to-istanbul@8.1.1:
dependencies:
'@types/istanbul-lib-coverage': 2.0.6
convert-source-map: 1.9.0
source-map: 0.7.4
validate-npm-package-license@3.0.4:
dependencies:
spdx-correct: 3.2.0
spdx-expression-parse: 3.0.1
vary@1.1.2: {}
w3c-hr-time@1.0.2:
dependencies:
browser-process-hrtime: 1.0.0
w3c-xmlserializer@2.0.0:
dependencies:
xml-name-validator: 3.0.0
walker@1.0.8:
dependencies:
makeerror: 1.0.12
web-streams-polyfill@3.3.3: {}
webidl-conversions@5.0.0: {}
webidl-conversions@6.1.0: {}
whatwg-encoding@1.0.5:
dependencies:
iconv-lite: 0.4.24
whatwg-mimetype@2.3.0: {}
whatwg-url@8.7.0:
dependencies:
lodash: 4.17.21
tr46: 2.1.0
webidl-conversions: 6.1.0
which-boxed-primitive@1.1.1:
dependencies:
is-bigint: 1.1.0
is-boolean-object: 1.2.2
is-number-object: 1.1.1
is-string: 1.1.1
is-symbol: 1.1.1
which-builtin-type@1.2.1:
dependencies:
call-bound: 1.0.4
function.prototype.name: 1.1.8
has-tostringtag: 1.0.2
is-async-function: 2.1.1
is-date-object: 1.1.0
is-finalizationregistry: 1.1.1
is-generator-function: 1.1.0
is-regex: 1.2.1
is-weakref: 1.1.1
isarray: 2.0.5
which-boxed-primitive: 1.1.1
which-collection: 1.0.2
which-typed-array: 1.1.19
which-collection@1.0.2:
dependencies:
is-map: 2.0.3
is-set: 2.0.3
is-weakmap: 2.0.2
is-weakset: 2.0.4
which-module@2.0.1: {}
which-typed-array@1.1.19:
dependencies:
available-typed-arrays: 1.0.7
call-bind: 1.0.8
call-bound: 1.0.4
for-each: 0.3.5
get-proto: 1.0.1
gopd: 1.2.0
has-tostringtag: 1.0.2
which@1.3.1:
dependencies:
isexe: 2.0.0
which@2.0.2:
dependencies:
isexe: 2.0.0
word-wrap@1.2.5: {}
wrap-ansi@6.2.0:
dependencies:
ansi-styles: 4.3.0
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi@7.0.0:
dependencies:
ansi-styles: 4.3.0
string-width: 4.2.3
strip-ansi: 6.0.1
wrappy@1.0.2: {}
write-file-atomic@3.0.3:
dependencies:
imurmurhash: 0.1.4
is-typedarray: 1.0.0
signal-exit: 3.0.7
typedarray-to-buffer: 3.1.5
write@1.0.3:
dependencies:
mkdirp: 0.5.6
ws@7.5.10: {}
xml-name-validator@3.0.0: {}
xmlchars@2.2.0: {}
y18n@4.0.3: {}
y18n@5.0.8: {}
yallist@3.1.1: {}
yargs-parser@18.1.3:
dependencies:
camelcase: 5.3.1
decamelize: 1.2.0
yargs-parser@20.2.9: {}
yargs@15.4.1:
dependencies:
cliui: 6.0.0
decamelize: 1.2.0
find-up: 4.1.0
get-caller-file: 2.0.5
require-directory: 2.1.1
require-main-filename: 2.0.0
set-blocking: 2.0.0
string-width: 4.2.3
which-module: 2.0.1
y18n: 4.0.3
yargs-parser: 18.1.3
yargs@16.2.0:
dependencies:
cliui: 7.0.4
escalade: 3.2.0
get-caller-file: 2.0.5
require-directory: 2.1.1
string-width: 4.2.3
y18n: 5.0.8
yargs-parser: 20.2.9
ylru@1.4.0: {}
================================================
FILE: src/Formidable.js
================================================
/* eslint-disable class-methods-use-this */
/* eslint-disable no-underscore-dangle */
import { init as cuid2init } from '@paralleldrive/cuid2';
import dezalgo from 'dezalgo';
import { EventEmitter } from 'node:events';
import fsPromises from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { StringDecoder } from 'node:string_decoder';
import once from 'once';
import FormidableError, * as errors from './FormidableError.js';
import PersistentFile from './PersistentFile.js';
import VolatileFile from './VolatileFile.js';
import DummyParser from './parsers/Dummy.js';
import MultipartParser from './parsers/Multipart.js';
import { json, multipart, octetstream, querystring } from './plugins/index.js';
const CUID2_FINGERPRINT = `${process.env.NODE_ENV}-${os.platform()}-${os.hostname()}`
const createId = cuid2init({ length: 25, fingerprint: CUID2_FINGERPRINT.toLowerCase() });
const DEFAULT_OPTIONS = {
maxFields: 1000,
maxFieldsSize: 20 * 1024 * 1024,
maxFiles: 1000,
maxFileSize: 200 * 1024 * 1024,
maxTotalFileSize: undefined,
minFileSize: 1,
allowEmptyFiles: false,
createDirsFromUploads: false,
keepExtensions: false,
encoding: 'utf-8',
hashAlgorithm: false,
uploadDir: os.tmpdir(),
enabledPlugins: [octetstream, querystring, multipart, json],
fileWriteStreamHandler: null,
defaultInvalidName: 'invalid-name',
filter(_part) {
return true;
},
filename: undefined,
};
function hasOwnProp(obj, key) {
return Object.prototype.hasOwnProperty.call(obj, key);
}
const decorateForceSequential = function (promiseCreator) {
/* forces a function that returns a promise to be sequential
useful for fs for example */
let lastPromise = Promise.resolve();
return async function (...x) {
const promiseWeAreWaitingFor = lastPromise;
let currentPromise;
let callback;
// we need to change lastPromise before await anything,
// otherwise 2 calls might wait the same thing
lastPromise = new Promise(function (resolve) {
callback = resolve;
});
await promiseWeAreWaitingFor;
currentPromise = promiseCreator(...x);
currentPromise.then(callback).catch(callback);
return currentPromise;
};
};
const createNecessaryDirectoriesAsync = decorateForceSequential(function (filePath) {
const directoryname = path.dirname(filePath);
return fsPromises.mkdir(directoryname, { recursive: true });
});
const invalidExtensionChar = (c) => {
const code = c.charCodeAt(0);
return !(
code === 46 || // .
(code >= 48 && code <= 57) ||
(code >= 65 && code <= 90) ||
(code >= 97 && code <= 122)
);
};
class IncomingForm extends EventEmitter {
constructor(options = {}) {
super();
this.options = { ...DEFAULT_OPTIONS, ...options };
if (!this.options.maxTotalFileSize) {
this.options.maxTotalFileSize = this.options.maxFileSize
}
const dir = path.resolve(
this.options.uploadDir || this.options.uploaddir || os.tmpdir(),
);
this.uploaddir = dir;
this.uploadDir = dir;
// initialize with null
[
'error',
'headers',
'type',
'bytesExpected',
'bytesReceived',
'_parser',
'req',
].forEach((key) => {
this[key] = null;
});
this._setUpRename();
this._flushing = 0;
this._fieldsSize = 0;
this._totalFileSize = 0;
this._plugins = [];
this.openedFiles = [];
this.options.enabledPlugins = []
.concat(this.options.enabledPlugins)
.filter(Boolean);
if (this.options.enabledPlugins.length === 0) {
throw new FormidableError(
'expect at least 1 enabled builtin plugin, see options.enabledPlugins',
errors.missingPlugin,
);
}
this.options.enabledPlugins.forEach((plugin) => {
this.use(plugin);
});
this._setUpMaxFields();
this._setUpMaxFiles();
this.ended = undefined;
this.type = undefined;
}
use(plugin) {
if (typeof plugin !== 'function') {
throw new FormidableError(
'.use: expect `plugin` to be a function',
errors.pluginFunction,
);
}
this._plugins.push(plugin.bind(this));
return this;
}
pause () {
try {
this.req.pause();
} catch (err) {
// the stream was destroyed
if (!this.ended) {
// before it was completed, crash & burn
this._error(err);
}
return false;
}
return true;
}
resume () {
try {
this.req.resume();
} catch (err) {
// the stream was destroyed
if (!this.ended) {
// before it was completed, crash & burn
this._error(err);
}
return false;
}
return true;
}
// returns a promise if no callback is provided
async parse(req, cb) {
this.req = req;
let promise;
// Setup callback first, so we don't miss anything from data events emitted immediately.
if (!cb) {
let resolveRef;
let rejectRef;
promise = new Promise((resolve, reject) => {
resolveRef = resolve;
rejectRef = reject;
});
cb = (err, fields, files) => {
if (err) {
rejectRef(err);
} else {
resolveRef([fields, files]);
}
}
}
const callback = once(dezalgo(cb));
this.fields = {};
const files = {};
this.on('field', (name, value) => {
if (this.type === 'multipart' || this.type === 'urlencoded') {
if (!hasOwnProp(this.fields, name)) {
this.fields[name] = [value];
} else {
this.fields[name].push(value);
}
} else {
this.fields[name] = value;
}
});
this.on('file', (name, file) => {
if (!hasOwnProp(files, name)) {
files[name] = [file];
} else {
files[name].push(file);
}
});
this.on('error', (err) => {
callback(err, this.fields, files);
});
this.on('end', () => {
callback(null, this.fields, files);
});
// Parse headers and setup the parser, ready to start listening for data.
await this.writeHeaders(req.headers);
let datafn = (buffer) => {
try {
this.write(buffer);
} catch (err) {
this._error(err);
}
}
let endfn = () => {
if (this.error) {
return;
}
if (this._parser) {
this._parser.end();
}
}
let pipe = null;
// Start listening for data.
req
.on('error', (err) => {
this._error(err);
})
.on('aborted', () => {
this.emit('aborted');
this._error(new FormidableError('Request aborted', errors.aborted));
})
switch (this.headers['content-encoding']) {
case "gzip":
pipe = require("zlib").createGunzip();
break;
case "deflate":
pipe = require("zlib").createInflate();
break;
case "br":
pipe = require("zlib").createBrotliDecompress();
break;
case "compress":
pipe = require("zlib").createUnzip();
break;
default:
pipe = node_stream.Transform({
transform: function (chunk, encoding, callback) {
callback(null, chunk);
}
})
}
pipe.on("data", datafn).on('end', endfn);
req.pipe(pipe)
if (promise) {
return promise;
}
return this;
}
async writeHeaders(headers) {
this.headers = headers;
this._parseContentLength();
await this._parseContentType();
if (!this._parser) {
this._error(
new FormidableError(
'no parser found',
errors.noParser,
415, // Unsupported Media Type
),
);
return;
}
this._parser.once('error', (error) => {
this._error(error);
});
}
write(buffer) {
if (this.error) {
return null;
}
if (!this._parser) {
this._error(
new FormidableError('uninitialized parser', errors.uninitializedParser),
);
return null;
}
this.bytesReceived += buffer.length;
this.emit('progress', this.bytesReceived, this.bytesExpected);
this._parser.write(buffer);
return this.bytesReceived;
}
onPart(part) {
// this method can be overwritten by the user
return this._handlePart(part);
}
async _handlePart(part) {
if (part.originalFilename && typeof part.originalFilename !== 'string') {
this._error(
new FormidableError(
`the part.originalFilename should be string when it exists`,
errors.filenameNotString,
),
);
return;
}
// This MUST check exactly for undefined. You can not change it to !part.originalFilename.
// todo: uncomment when switch tests to Jest
// console.log(part);
// ? NOTE(@tunnckocore): no it can be any falsey value, it most probably depends on what's returned
// from somewhere else. Where recently I changed the return statements
// and such thing because code style
// ? NOTE(@tunnckocore): or even better, if there is no mimetype, then it's for sure a field
// ? NOTE(@tunnckocore): originalFilename is an empty string when a field?
if (!part.mimetype) {
let value = '';
const decoder = new StringDecoder(
part.transferEncoding || this.options.encoding,
);
part.on('data', (buffer) => {
this._fieldsSize += buffer.length;
if (this._fieldsSize > this.options.maxFieldsSize) {
this._error(
new FormidableError(
`options.maxFieldsSize (${this.options.maxFieldsSize} bytes) exceeded, received ${this._fieldsSize} bytes of field data`,
errors.maxFieldsSizeExceeded,
413, // Payload Too Large
),
);
return;
}
value += decoder.write(buffer);
});
part.on('end', () => {
this.emit('field', part.name, value);
});
return;
}
if (!this.options.filter(part)) {
return;
}
this._flushing += 1;
let fileSize = 0;
const newFilename = this._getNewName(part);
const filepath = this._joinDirectoryName(newFilename);
const file = await this._newFile({
newFilename,
filepath,
originalFilename: part.originalFilename,
mimetype: part.mimetype,
});
file.on('error', (err) => {
this._error(err);
});
this.emit('fileBegin', part.name, file);
file.open();
this.openedFiles.push(file);
part.on('data', (buffer) => {
this._totalFileSize += buffer.length;
fileSize += buffer.length;
if (this._totalFileSize > this.options.maxTotalFileSize) {
this._error(
new FormidableError(
`options.maxTotalFileSize (${this.options.maxTotalFileSize} bytes) exceeded, received ${this._totalFileSize} bytes of file data`,
errors.biggerThanTotalMaxFileSize,
413,
),
);
return;
}
if (buffer.length === 0) {
return;
}
this.pause();
file.write(buffer, () => {
this.resume();
});
});
part.on('end', () => {
if (!this.options.allowEmptyFiles && fileSize === 0) {
this._error(
new FormidableError(
`options.allowEmptyFiles is false, file size should be greater than 0`,
errors.noEmptyFiles,
400,
),
);
return;
}
if (fileSize < this.options.minFileSize) {
this._error(
new FormidableError(
`options.minFileSize (${this.options.minFileSize} bytes) inferior, received ${fileSize} bytes of file data`,
errors.smallerThanMinFileSize,
400,
),
);
return;
}
if (fileSize > this.options.maxFileSize) {
this._error(
new FormidableError(
`options.maxFileSize (${this.options.maxFileSize} bytes), received ${fileSize} bytes of file data`,
errors.biggerThanMaxFileSize,
413,
),
);
return;
}
file.end(() => {
this._flushing -= 1;
this.emit('file', part.name, file);
this._maybeEnd();
});
});
}
// eslint-disable-next-line max-statements
async _parseContentType() {
if (this.bytesExpected === 0) {
this._parser = new DummyParser(this, this.options);
return;
}
if (!this.headers['content-type']) {
this._error(
new FormidableError(
'bad content-type header, no content-type',
errors.missingContentType,
400,
),
);
return;
}
new DummyParser(this, this.options);
const results = [];
await Promise.all(this._plugins.map(async (plugin, idx) => {
let pluginReturn = null;
try {
pluginReturn = await plugin(this, this.options) || this;
} catch (err) {
// directly throw from the `form.parse` method;
// there is no other better way, except a handle through options
const error = new FormidableError(
`plugin on index ${idx} failed with: ${err.message}`,
errors.pluginFailed,
500,
);
error.idx = idx;
throw error;
}
Object.assign(this, pluginReturn);
// todo: use Set/Map and pass plugin name instead of the `idx` index
this.emit('plugin', idx, pluginReturn);
}));
this.emit('pluginsResults', results);
}
_error(err, eventName = 'error') {
if (this.error || this.ended) {
return;
}
this.req = null;
this.error = err;
this.emit(eventName, err);
this.openedFiles.forEach((file) => {
file.destroy();
});
}
_parseContentLength() {
this.bytesReceived = 0;
if (this.headers['content-length']) {
this.bytesExpected = parseInt(this.headers['content-length'], 10);
} else if (this.headers['transfer-encoding'] === undefined) {
this.bytesExpected = 0;
}
if (this.bytesExpected !== null) {
this.emit('progress', this.bytesReceived, this.bytesExpected);
}
}
_newParser() {
return new MultipartParser(this.options);
}
async _newFile({ filepath, originalFilename, mimetype, newFilename }) {
if (this.options.fileWriteStreamHandler) {
return new VolatileFile({
newFilename,
filepath,
originalFilename,
mimetype,
createFileWriteStream: this.options.fileWriteStreamHandler,
hashAlgorithm: this.options.hashAlgorithm,
});
}
if (this.options.createDirsFromUploads) {
try {
await createNecessaryDirectoriesAsync(filepath);
} catch (errorCreatingDir) {
this._error(new FormidableError(
`cannot create directory`,
errors.cannotCreateDir,
409,
));
}
}
return new PersistentFile({
newFilename,
filepath,
originalFilename,
mimetype,
hashAlgorithm: this.options.hashAlgorithm,
});
}
_getFileName(headerValue) {
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
const m = headerValue.match(
/\bfilename=("(.*?)"|([^()<>{}[\]@,;:"?=\s/\t]+))($|;\s)/i,
);
if (!m) return null;
const match = m[2] || m[3] || '';
let originalFilename = match.substr(match.lastIndexOf('\\') + 1);
originalFilename = originalFilename.replace(/%22/g, '"');
originalFilename = originalFilename.replace(/([\d]{4});/g, (_, code) =>
String.fromCharCode(code),
);
return originalFilename;
}
// able to get composed extension with multiple dots
// "a.b.c" -> ".b.c"
// as opposed to path.extname -> ".c"
_getExtension(str) {
if (!str) {
return '';
}
const basename = path.basename(str);
const firstDot = basename.indexOf('.');
const lastDot = basename.lastIndexOf('.');
let rawExtname = path.extname(basename);
if (firstDot !== lastDot) {
rawExtname = basename.slice(firstDot);
}
let filtered;
const firstInvalidIndex = Array.from(rawExtname).findIndex(invalidExtensionChar);
if (firstInvalidIndex === -1) {
filtered = rawExtname;
} else {
filtered = rawExtname.substring(0, firstInvalidIndex);
}
if (filtered === '.') {
return '';
}
return filtered;
}
_joinDirectoryName(name) {
const resolvedDir = path.resolve(this.uploadDir);
const resolvedPath = path.resolve(resolvedDir, name);
// prevent directory traversal attacks
// use resolvedDir + path.sep to avoid prefix collisions with sibling directories
// e.g. uploadDir "/tmp/uploads" should not allow writes to "/tmp/uploads-evil/"
if (resolvedPath === resolvedDir || !resolvedPath.startsWith(resolvedDir + path.sep)) {
return path.join(this.uploadDir, this.options.defaultInvalidName);
}
return resolvedPath;
}
_setUpRename() {
const hasRename = typeof this.options.filename === 'function';
if (hasRename) {
this._getNewName = (part) => {
let ext = '';
let name = this.options.defaultInvalidName;
if (part.originalFilename) {
// can be null
({ ext, name } = path.parse(part.originalFilename));
if (this.options.keepExtensions !== true) {
ext = '';
}
}
return this.options.filename.call(this, name, ext, part, this);
};
} else {
this._getNewName = (part) => {
const name = createId();
if (part && this.options.keepExtensions) {
const originalFilename =
typeof part === 'string' ? part : part.originalFilename;
return `${name}${this._getExtension(originalFilename)}`;
}
return name;
};
}
}
_setUpMaxFields() {
if (this.options.maxFields !== Infinity) {
let fieldsCount = 0;
this.on('field', () => {
fieldsCount += 1;
if (fieldsCount > this.options.maxFields) {
this._error(
new FormidableError(
`options.maxFields (${this.options.maxFields}) exceeded`,
errors.maxFieldsExceeded,
413,
),
);
}
});
}
}
_setUpMaxFiles() {
if (this.options.maxFiles !== Infinity) {
let fileCount = 0;
this.on('fileBegin', () => {
fileCount += 1;
if (fileCount > this.options.maxFiles) {
this._error(
new FormidableError(
`options.maxFiles (${this.options.maxFiles}) exceeded`,
errors.maxFilesExceeded,
413,
),
);
}
});
}
}
_maybeEnd() {
if (!this.ended || this._flushing || this.error) {
return;
}
this.req = null;
this.emit('end');
}
}
export default IncomingForm;
export { DEFAULT_OPTIONS };
================================================
FILE: src/FormidableError.js
================================================
const missingPlugin = 1000;
const pluginFunction = 1001;
const aborted = 1002;
const noParser = 1003;
const uninitializedParser = 1004;
const filenameNotString = 1005;
const maxFieldsSizeExceeded = 1006;
const maxFieldsExceeded = 1007;
const smallerThanMinFileSize = 1008;
const biggerThanTotalMaxFileSize = 1009;
const noEmptyFiles = 1010;
const missingContentType = 1011;
const malformedMultipart = 1012;
const missingMultipartBoundary = 1013;
const unknownTransferEncoding = 1014;
const maxFilesExceeded = 1015;
const biggerThanMaxFileSize = 1016;
const pluginFailed = 1017;
const cannotCreateDir = 1018;
const FormidableError = class extends Error {
constructor(message, internalCode, httpCode = 500) {
super(message);
this.code = internalCode;
this.httpCode = httpCode;
}
};
export {
missingPlugin,
pluginFunction,
aborted,
noParser,
uninitializedParser,
filenameNotString,
maxFieldsSizeExceeded,
maxFieldsExceeded,
maxFilesExceeded,
smallerThanMinFileSize,
biggerThanMaxFileSize,
noEmptyFiles,
missingContentType,
malformedMultipart,
missingMultipartBoundary,
unknownTransferEncoding,
biggerThanTotalMaxFileSize,
pluginFailed,
cannotCreateDir,
};
export default FormidableError;
================================================
FILE: src/PersistentFile.js
================================================
/* eslint-disable no-underscore-dangle */
import fs from 'node:fs';
import crypto from 'node:crypto';
import { EventEmitter } from 'node:events';
class PersistentFile extends EventEmitter {
constructor({ filepath, newFilename, originalFilename, mimetype, hashAlgorithm }) {
super();
this.lastModifiedDate = null;
Object.assign(this, { filepath, newFilename, originalFilename, mimetype, hashAlgorithm });
this.size = 0;
this._writeStream = null;
if (typeof this.hashAlgorithm === 'string') {
this.hash = crypto.createHash(this.hashAlgorithm);
} else {
this.hash = null;
}
}
open() {
this._writeStream = fs.createWriteStream(this.filepath);
this._writeStream.on('error', (err) => {
this.emit('error', err);
});
}
toJSON() {
const json = {
size: this.size,
filepath: this.filepath,
newFilename: this.newFilename,
mimetype: this.mimetype,
mtime: this.lastModifiedDate,
length: this.length,
originalFilename: this.originalFilename,
};
if (this.hash && this.hash !== '') {
json.hash = this.hash;
}
return json;
}
toString() {
return `PersistentFile: ${this.newFilename}, Original: ${this.originalFilename}, Path: ${this.filepath}`;
}
write(buffer, cb) {
if (this.hash) {
this.hash.update(buffer);
}
if (this._writeStream.closed) {
cb();
return;
}
this._writeStream.write(buffer, () => {
this.lastModifiedDate = new Date();
this.size += buffer.length;
this.emit('progress', this.size);
cb();
});
}
end(cb) {
if (this.hash) {
this.hash = this.hash.digest('hex');
}
this._writeStream.end(() => {
this.emit('end');
cb();
});
}
destroy() {
this._writeStream.destroy();
const filepath = this.filepath;
setTimeout(function () {
fs.unlink(filepath, () => {});
}, 1)
}
}
export default PersistentFile;
================================================
FILE: src/VolatileFile.js
================================================
/* eslint-disable no-underscore-dangle */
import { createHash } from 'node:crypto';
import { EventEmitter } from 'node:events';
class VolatileFile extends EventEmitter {
constructor({ filepath, newFilename, originalFilename, mimetype, hashAlgorithm, createFileWriteStream }) {
super();
this.lastModifiedDate = null;
Object.assign(this, { filepath, newFilename, originalFilename, mimetype, hashAlgorithm, createFileWriteStream });
this.size = 0;
this._writeStream = null;
if (typeof this.hashAlgorithm === 'string') {
this.hash = createHash(this.hashAlgorithm);
} else {
this.hash = null;
}
}
open() {
this._writeStream = this.createFileWriteStream(this);
this._writeStream.on('error', (err) => {
this.emit('error', err);
});
}
destroy() {
this._writeStream.destroy();
}
toJSON() {
const json = {
size: this.size,
newFilename: this.newFilename,
length: this.length,
originalFilename: this.originalFilename,
mimetype: this.mimetype,
};
if (this.hash && this.hash !== '') {
json.hash = this.hash;
}
return json;
}
toString() {
return `VolatileFile: ${this.originalFilename}`;
}
write(buffer, cb) {
if (this.hash) {
this.hash.update(buffer);
}
if (this._writeStream.closed || this._writeStream.destroyed) {
cb();
return;
}
this._writeStream.write(buffer, () => {
this.size += buffer.length;
this.emit('progress', this.size);
cb();
});
}
end(cb) {
if (this.hash) {
this.hash = this.hash.digest('hex');
}
this._writeStream.end(() => {
this.emit('end');
cb();
});
}
}
export default VolatileFile;
================================================
FILE: src/helpers/firstValues.js
================================================
import { multipartType } from '../plugins/multipart.js';
import { querystringType } from '../plugins/querystring.js';
const firstValues = (form, fields, exceptions = []) => {
if (form.type !== querystringType && form.type !== multipartType) {
return fields;
}
return Object.fromEntries(
Object.entries(fields).map(([key, value]) => {
if (exceptions.includes(key)) {
return [key, value];
}
return [key, value[0]];
}),
);
};
export { firstValues };
================================================
FILE: src/helpers/readBooleans.js
================================================
const readBooleans = (fields, listOfBooleans) => {
// html forms do not send off at all
const fieldsWithBooleans = { ...fields };
listOfBooleans.forEach((key) => {
fieldsWithBooleans[key] = fields[key] === `on` || fields[key] === true;
});
return fieldsWithBooleans;
};
export { readBooleans };
================================================
FILE: src/index.js
================================================
import PersistentFile from './PersistentFile.js';
import VolatileFile from './VolatileFile.js';
import Formidable, { DEFAULT_OPTIONS } from './Formidable.js';
// make it available without requiring the `new` keyword
// if you want it access `const formidable.IncomingForm` as v1
const formidable = (...args) => new Formidable(...args);
const {enabledPlugins} = DEFAULT_OPTIONS;
export default formidable;
export {
PersistentFile as File,
PersistentFile,
VolatileFile,
Formidable,
// alias
Formidable as IncomingForm,
// as named
formidable,
// misc
DEFAULT_OPTIONS as defaultOptions,
enabledPlugins,
};
export * from './parsers/index.js';
export * from './plugins/index.js';
export * as errors from './FormidableError.js';
================================================
FILE: src/parsers/Dummy.js
================================================
/* eslint-disable no-underscore-dangle */
import { Transform } from 'node:stream';
class DummyParser extends Transform {
constructor(incomingForm, options = {}) {
super();
this.globalOptions = { ...options };
this.incomingForm = incomingForm;
}
_flush(callback) {
this.incomingForm.ended = true;
this.incomingForm._maybeEnd();
callback();
}
}
export default DummyParser;
================================================
FILE: src/parsers/JSON.js
================================================
/* eslint-disable no-underscore-dangle */
import { Transform } from 'node:stream';
class JSONParser extends Transform {
constructor(options = {}) {
super({ readableObjectMode: true });
this.chunks = [];
this.globalOptions = { ...options };
}
_transform(chunk, encoding, callback) {
this.chunks.push(String(chunk)); // todo consider using a string decoder
callback();
}
_flush(callback) {
try {
const fields = JSON.parse(this.chunks.join(''));
this.push(fields);
} catch (e) {
callback(e);
return;
}
this.chunks = null;
callback();
}
}
export default JSONParser;
================================================
FILE: src/parsers/Multipart.js
================================================
/* eslint-disable no-fallthrough */
/* eslint-disable no-bitwise */
/* eslint-disable no-plusplus */
/* eslint-disable no-underscore-dangle */
import { Transform } from 'node:stream';
import * as errors from '../FormidableError.js';
import FormidableError from '../FormidableError.js';
let s = 0;
const STATE = {
PARSER_UNINITIALIZED: s++,
START: s++,
START_BOUNDARY: s++,
HEADER_FIELD_START: s++,
HEADER_FIELD: s++,
HEADER_VALUE_START: s++,
HEADER_VALUE: s++,
HEADER_VALUE_ALMOST_DONE: s++,
HEADERS_ALMOST_DONE: s++,
PART_DATA_START: s++,
PART_DATA: s++,
PART_END: s++,
END: s++,
};
let f = 1;
const FBOUNDARY = { PART_BOUNDARY: f, LAST_BOUNDARY: (f *= 2) };
const LF = 10;
const CR = 13;
const SPACE = 32;
const HYPHEN = 45;
const COLON = 58;
const A = 97;
const Z = 122;
function lower(c) {
return c | 0x20;
}
export const STATES = {};
Object.keys(STATE).forEach((stateName) => {
STATES[stateName] = STATE[stateName];
});
class MultipartParser extends Transform {
constructor(options = {}) {
super({ readableObjectMode: true });
this.boundary = null;
this.boundaryChars = null;
this.lookbehind = null;
this.bufferLength = 0;
this.state = STATE.PARSER_UNINITIALIZED;
this.globalOptions = { ...options };
this.index = null;
this.flags = 0;
}
_endUnexpected() {
return new FormidableError(
`MultipartParser.end(): stream ended unexpectedly: ${this.explain()}`,
errors.malformedMultipart,
400,
);
}
_flush(done) {
if (
(this.state === STATE.HEADER_FIELD_START && this.index === 0) ||
(this.state === STATE.PART_DATA && this.index === this.boundary.length)
) {
this._handleCallback('partEnd');
this._handleCallback('end');
done();
} else if (this.state !== STATE.END) {
done(this._endUnexpected());
} else {
done();
}
}
initWithBoundary(str) {
this.boundary = Buffer.from(`\r\n--${str}`);
this.lookbehind = Buffer.alloc(this.boundary.length + 8);
this.state = STATE.START;
this.boundaryChars = {};
for (let i = 0; i < this.boundary.length; i++) {
this.boundaryChars[this.boundary[i]] = true;
}
}
// eslint-disable-next-line max-params
_handleCallback(name, buf, start, end) {
if (start !== undefined && start === end) {
return;
}
this.push({ name, buffer: buf, start, end });
}
// eslint-disable-next-line max-statements
_transform(buffer, _, done) {
let i = 0;
let prevIndex = this.index;
let { index, state, flags } = this;
const { lookbehind, boundary, boundaryChars } = this;
const boundaryLength = boundary.length;
const boundaryEnd = boundaryLength - 1;
this.bufferLength = buffer.length;
let c = null;
let cl = null;
const setMark = (name, idx) => {
this[`${name}Mark`] = typeof idx === 'number' ? idx : i;
};
const clearMarkSymbol = (name) => {
delete this[`${name}Mark`];
};
const dataCallback = (name, shouldClear) => {
const markSymbol = `${name}Mark`;
if (!(markSymbol in this)) {
return;
}
if (!shouldClear) {
this._handleCallback(name, buffer, this[markSymbol], buffer.length);
setMark(name, 0);
} else {
this._handleCallback(name, buffer, this[markSymbol], i);
clearMarkSymbol(name);
}
};
for (i = 0; i < this.bufferLength; i++) {
c = buffer[i];
switch (state) {
case STATE.PARSER_UNINITIALIZED:
done(this._endUnexpected());
return;
case STATE.START:
index = 0;
state = STATE.START_BOUNDARY;
case STATE.START_BOUNDARY:
if (index === boundary.length - 2) {
if (c === HYPHEN) {
flags |= FBOUNDARY.LAST_BOUNDARY;
} else if (c !== CR) {
done(this._endUnexpected());
return;
}
index++;
break;
} else if (index - 1 === boundary.length - 2) {
if (flags & FBOUNDARY.LAST_BOUNDARY && c === HYPHEN) {
this._handleCallback('end');
state = STATE.END;
flags = 0;
} else if (!(flags & FBOUNDARY.LAST_BOUNDARY) && c === LF) {
index = 0;
this._handleCallback('partBegin');
state = STATE.HEADER_FIELD_START;
} else {
done(this._endUnexpected());
return;
}
break;
}
if (c !== boundary[index + 2]) {
index = -2;
}
if (c === boundary[index + 2]) {
index++;
}
break;
case STATE.HEADER_FIELD_START:
state = STATE.HEADER_FIELD;
setMark('headerField');
index = 0;
case STATE.HEADER_FIELD:
if (c === CR) {
clearMarkSymbol('headerField');
state = STATE.HEADERS_ALMOST_DONE;
break;
}
index++;
if (c === HYPHEN) {
break;
}
if (c === COLON) {
if (index === 1) {
// empty header field
done(this._endUnexpected());
return;
}
dataCallback('headerField', true);
state = STATE.HEADER_VALUE_START;
break;
}
cl = lower(c);
if (cl < A || cl > Z) {
done(this._endUnexpected());
return;
}
break;
case STATE.HEADER_VALUE_START:
if (c === SPACE) {
break;
}
setMark('headerValue');
state = STATE.HEADER_VALUE;
case STATE.HEADER_VALUE:
if (c === CR) {
dataCallback('headerValue', true);
this._handleCallback('headerEnd');
state = STATE.HEADER_VALUE_ALMOST_DONE;
}
break;
case STATE.HEADER_VALUE_ALMOST_DONE:
if (c !== LF) {
done(this._endUnexpected());
return;
}
state = STATE.HEADER_FIELD_START;
break;
case STATE.HEADERS_ALMOST_DONE:
if (c !== LF) {
done(this._endUnexpected());
return;
}
this._handleCallback('headersEnd');
state = STATE.PART_DATA_START;
break;
case STATE.PART_DATA_START:
state = STATE.PART_DATA;
setMark('partData');
case STATE.PART_DATA:
prevIndex = index;
if (index === 0) {
// boyer-moore derived algorithm to safely skip non-boundary data
i += boundaryEnd;
while (i < this.bufferLength && !(buffer[i] in boundaryChars)) {
i += boundaryLength;
}
i -= boundaryEnd;
c = buffer[i];
}
if (index < boundary.length) {
if (boundary[index] === c) {
if (index === 0) {
dataCallback('partData', true);
}
index++;
} else {
index = 0;
}
} else if (index === boundary.length) {
index++;
if (c === CR) {
// CR = part boundary
flags |= FBOUNDARY.PART_BOUNDARY;
} else if (c === HYPHEN) {
// HYPHEN = end boundary
flags |= FBOUNDARY.LAST_BOUNDARY;
} else {
index = 0;
}
} else if (index - 1 === boundary.length) {
if (flags & FBOUNDARY.PART_BOUNDARY) {
index = 0;
if (c === LF) {
// unset the PART_BOUNDARY flag
flags &= ~FBOUNDARY.PART_BOUNDARY;
this._handleCallback('partEnd');
this._handleCallback('partBegin');
state = STATE.HEADER_FIELD_START;
break;
}
} else if (flags & FBOUNDARY.LAST_BOUNDARY) {
if (c === HYPHEN) {
this._handleCallback('partEnd');
this._handleCallback('end');
state = STATE.END;
flags = 0;
} else {
index = 0;
}
} else {
index = 0;
}
}
if (index > 0) {
// when matching a possible boundary, keep a lookbehind reference
// in case it turns out to be a false lead
lookbehind[index - 1] = c;
} else if (prevIndex > 0) {
// if our boundary turned out to be rubbish, the captured lookbehind
// belongs to partData
this._handleCallback('partData', lookbehind, 0, prevIndex);
prevIndex = 0;
setMark('partData');
// reconsider the current character even so it interrupted the sequence
// it could be the beginning of a new sequence
i--;
}
break;
case STATE.END:
break;
default:
done(this._endUnexpected());
return;
}
}
dataCallback('headerField');
dataCallback('headerValue');
dataCallback('partData');
this.index = index;
this.state = state;
this.flags = flags;
done();
return this.bufferLength;
}
explain() {
return `state = ${MultipartParser.stateToString(this.state)}`;
}
}
// eslint-disable-next-line consistent-return
MultipartParser.stateToString = (stateNumber) => {
// eslint-disable-next-line no-restricted-syntax, guard-for-in
for (const stateName in STATE) {
const number = STATE[stateName];
if (number === stateNumber) return stateName;
}
};
export default Object.assign(MultipartParser, { STATES });
================================================
FILE: src/parsers/OctetStream.js
================================================
import { PassThrough } from 'node:stream';
class OctetStreamParser extends PassThrough {
constructor(options = {}) {
super();
this.globalOptions = { ...options };
}
}
export default OctetStreamParser;
================================================
FILE: src/parsers/Querystring.js
================================================
/* eslint-disable no-underscore-dangle */
import { Transform } from 'node:stream';
// This is a buffering parser, have a look at StreamingQuerystring.js for a streaming parser
class QuerystringParser extends Transform {
constructor(options = {}) {
super({ readableObjectMode: true });
this.globalOptions = { ...options };
this.buffer = '';
this.bufferLength = 0;
}
_transform(buffer, encoding, callback) {
this.buffer += buffer.toString('ascii');
this.bufferLength = this.buffer.length;
callback();
}
_flush(callback) {
const fields = new URLSearchParams(this.buffer);
for (const [key, value] of fields) {
this.push({
key,
value,
});
}
this.buffer = '';
callback();
}
}
export default QuerystringParser;
================================================
FILE: src/parsers/StreamingQuerystring.js
================================================
// not used
/* eslint-disable no-underscore-dangle */
import { Transform } from 'node:stream';
import FormidableError, { maxFieldsSizeExceeded } from '../FormidableError.js';
const AMPERSAND = 38;
const EQUALS = 61;
class QuerystringParser extends Transform {
constructor(options = {}) {
super({ readableObjectMode: true });
const { maxFieldSize } = options;
this.maxFieldLength = maxFieldSize;
this.buffer = Buffer.from('');
this.fieldCount = 0;
this.sectionStart = 0;
this.key = '';
this.readingKey = true;
}
_transform(buffer, encoding, callback) {
let len = buffer.length;
if (this.buffer && this.buffer.length) {
// we have some data left over from the last write which we are in the middle of processing
len += this.buffer.length;
buffer = Buffer.concat([this.buffer, buffer], len);
}
for (let i = this.buffer.length || 0; i < len; i += 1) {
const c = buffer[i];
if (this.readingKey) {
// KEY, check for =
if (c === EQUALS) {
this.key = this.getSection(buffer, i);
this.readingKey = false;
this.sectionStart = i + 1;
} else if (c === AMPERSAND) {
// just key, no value. Prepare to read another key
this.emitField(this.getSection(buffer, i));
this.sectionStart = i + 1;
}
// VALUE, check for &
} else if (c === AMPERSAND) {
this.emitField(this.key, this.getSection(buffer, i));
this.sectionStart = i + 1;
}
if (
this.maxFieldLength &&
i - this.sectionStart === this.maxFieldLength
) {
callback(
new FormidableError(
`${
this.readingKey ? 'Key' : `Value for ${this.key}`
} longer than maxFieldLength`,
),
maxFieldsSizeExceeded,
413,
);
}
}
// Prepare the remaining key or value (from sectionStart to the end) for the next write() or for end()
len -= this.sectionStart;
if (len) {
// i.e. Unless the last character was a & or =
this.buffer = Buffer.from(this.buffer, 0, this.sectionStart);
} else this.buffer = null;
this.sectionStart = 0;
callback();
}
_flush(callback) {
// Emit the last field
if (this.readingKey) {
// we only have a key if there's something in the buffer. We definitely have no value
if (this.buffer && this.buffer.length) {
this.emitField(this.buffer.toString('ascii'));
}
} else {
// We have a key, we may or may not have a value
this.emitField(
this.key,
this.buffer && this.buffer.length && this.buffer.toString('ascii'),
);
}
this.buffer = '';
callback();
}
getSection(buffer, i) {
if (i === this.sectionStart) return '';
return buffer.toString('ascii', this.sectionStart, i);
}
emitField(key, val) {
this.key = '';
this.readingKey = true;
this.push({ key, value: val || '' });
}
}
export default QuerystringParser;
// const q = new QuerystringParser({maxFieldSize: 100});
// (async function() {
// for await (const chunk of q) {
// console.log(chunk);
// }
// })();
// q.write("a=b&c=d")
// q.end()
================================================
FILE: src/parsers/index.js
================================================
import JSONParser from './JSON.js';
import DummyParser from './Dummy.js';
import MultipartParser from './Multipart.js';
import OctetStreamParser from './OctetStream.js';
import QueryStringParser from './Querystring.js';
export {
JSONParser,
DummyParser,
MultipartParser,
OctetStreamParser,
OctetStreamParser as OctetstreamParser,
QueryStringParser,
QueryStringParser as QuerystringParser,
};
================================================
FILE: src/plugins/index.js
================================================
import octetstream from './octetstream.js';
import querystring from './querystring.js';
import multipart from './multipart.js';
import json from './json.js';
export { octetstream, querystring, multipart, json };
================================================
FILE: src/plugins/json.js
================================================
/* eslint-disable no-underscore-dangle */
import JSONParser from '../parsers/JSON.js';
export const jsonType = 'json';
// the `options` is also available through the `this.options` / `formidable.options`
export default function plugin(formidable, options) {
// the `this` context is always formidable, as the first argument of a plugin
// but this allows us to customize/test each plugin
/* istanbul ignore next */
const self = this || formidable;
if (/^[^;]*json/i.test(self.headers['content-type'])) {
init.call(self, self, options);
}
return self;
};
// Note that it's a good practice (but it's up to you) to use the `this.options` instead
// of the passed `options` (second) param, because when you decide
// to test the plugin you can pass custom `this` context to it (and so `this.options`)
function init(_self, _opts) {
this.type = jsonType;
const parser = new JSONParser(this.options);
parser.on('data', (fields) => {
this.fields = fields;
});
parser.once('end', () => {
this.ended = true;
this._maybeEnd();
});
this._parser = parser;
}
================================================
FILE: src/plugins/multipart.js
================================================
/* eslint-disable no-underscore-dangle */
import { Stream } from 'node:stream';
import MultipartParser from '../parsers/Multipart.js';
import * as errors from '../FormidableError.js';
import FormidableError from '../FormidableError.js';
export const multipartType = 'multipart';
// the `options` is also available through the `options` / `formidable.options`
export default function plugin(formidable, options) {
// the `this` context is always formidable, as the first argument of a plugin
// but this allows us to customize/test each plugin
/* istanbul ignore next */
const self = this || formidable;
// NOTE: we (currently) support both multipart/form-data and multipart/related
const multipart = /^[^;]*multipart/i.test(self.headers['content-type']);
if (multipart) {
const m = self.headers['content-type'].match(
/boundary=(?:"([^"]+)"|([^;]+))/i,
);
if (m) {
const initMultipart = createInitMultipart(m[1] || m[2]);
initMultipart.call(self, self, options); // lgtm [js/superfluous-trailing-arguments]
} else {
const err = new FormidableError(
'bad content-type header, no multipart boundary',
errors.missingMultipartBoundary,
400,
);
self._error(err);
}
}
return self;
}
// Note that it's a good practice (but it's up to you) to use the `this.options` instead
// of the passed `options` (second) param, because when you decide
// to test the plugin you can pass custom `this` context to it (and so `this.options`)
function createInitMultipart(boundary) {
return function initMultipart() {
this.type = multipartType;
const parser = new MultipartParser(this.options);
let headerField;
let headerValue;
let part;
parser.initWithBoundary(boundary);
// eslint-disable-next-line max-statements, consistent-return
parser.on('data', async ({ name, buffer, start, end }) => {
if (name === 'partBegin') {
part = new Stream();
part.readable = true;
part.headers = {};
part.name = null;
part.originalFilename = null;
part.mimetype = null;
part.transferEncoding = this.options.encoding;
part.transferBuffer = '';
headerField = '';
headerValue = '';
} else if (name === 'headerField') {
headerField += buffer.toString(this.options.encoding, start, end);
} else if (name === 'headerValue') {
headerValue += buffer.toString(this.options.encoding, start, end);
} else if (name === 'headerEnd') {
headerField = headerField.toLowerCase();
part.headers[headerField] = headerValue;
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
const m = headerValue.match(
// eslint-disable-next-line no-useless-escape
/\bname=("([^"]*)"|([^\(\)<>@,;:\\"\/\[\]\?=\{\}\s\t/]+))/i,
);
if (headerField === 'content-disposition') {
if (m) {
part.name = m[2] || m[3] || '';
}
part.originalFilename = this._getFileName(headerValue);
} else if (headerField === 'content-type') {
part.mimetype = headerValue;
} else if (headerField === 'content-transfer-encoding') {
part.transferEncoding = headerValue.toLowerCase();
}
headerField = '';
headerValue = '';
} else if (name === 'headersEnd') {
switch (part.transferEncoding) {
case 'binary':
case '7bit':
case '8bit':
case 'utf-8': {
const dataPropagation = (ctx) => {
if (ctx.name === 'partData') {
part.emit('data', ctx.buffer.slice(ctx.start, ctx.end));
}
};
const dataStopPropagation = (ctx) => {
if (ctx.name === 'partEnd') {
part.emit('end');
parser.off('data', dataPropagation);
parser.off('data', dataStopPropagation);
}
};
parser.on('data', dataPropagation);
parser.on('data', dataStopPropagation);
break;
}
case 'base64': {
const dataPropagation = (ctx) => {
if (ctx.name === 'partData') {
part.transferBuffer += ctx.buffer
.slice(ctx.start, ctx.end)
.toString('ascii');
/*
four bytes (chars) in base64 converts to three bytes in binary
encoding. So we should always work with a number of bytes that
can be divided by 4, it will result in a number of bytes that
can be divided vy 3.
*/
const offset = parseInt(part.transferBuffer.length / 4, 10) * 4;
part.emit(
'data',
Buffer.from(
part.transferBuffer.substring(0, offset),
'base64',
),
);
part.transferBuffer = part.transferBuffer.substring(offset);
}
};
const dataStopPropagation = (ctx) => {
if (ctx.name === 'partEnd') {
part.emit('data', Buffer.from(part.transferBuffer, 'base64'));
part.emit('end');
parser.off('data', dataPropagation);
parser.off('data', dataStopPropagation);
}
};
parser.on('data', dataPropagation);
parser.on('data', dataStopPropagation);
break;
}
default:
return this._error(
new FormidableError(
'unknown transfer-encoding',
errors.unknownTransferEncoding,
501,
),
);
}
this._parser.pause();
await this.onPart(part);
this._parser.resume();
} else if (name === 'end') {
this.ended = true;
this._maybeEnd();
}
});
this._parser = parser;
};
}
================================================
FILE: src/plugins/octetstream.js
================================================
/* eslint-disable no-underscore-dangle */
import OctetStreamParser from '../parsers/OctetStream.js';
export const octetStreamType = 'octet-stream';
// the `options` is also available through the `options` / `formidable.options`
export default async function plugin(formidable, options) {
// the `this` context is always formidable, as the first argument of a plugin
// but this allows us to customize/test each plugin
/* istanbul ignore next */
const self = this || formidable;
if (/^[^;]*octet-stream/i.test(self.headers['content-type'])) {
await init.call(self, self, options);
}
return self;
}
// Note that it's a good practice (but it's up to you) to use the `this.options` instead
// of the passed `options` (second) param, because when you decide
// to test the plugin you can pass custom `this` context to it (and so `this.options`)
async function init(_self, _opts) {
this.type = octetStreamType;
const originalFilename = this.headers['x-file-name'];
const mimetype = this.headers['content-type'];
const thisPart = {
originalFilename,
mimetype,
};
const newFilename = this._getNewName(thisPart);
const filepath = this._joinDirectoryName(newFilename);
const file = await this._newFile({
newFilename,
filepath,
originalFilename,
mimetype,
});
this.emit('fileBegin', originalFilename, file);
file.open();
this.openedFiles.push(file);
this._flushing += 1;
this._parser = new OctetStreamParser(this.options);
// Keep track of writes that haven't finished so we don't emit the file before it's done being written
let outstandingWrites = 0;
this._parser.on('data', (buffer) => {
this.pause();
outstandingWrites += 1;
file.write(buffer, () => {
outstandingWrites -= 1;
this.resume();
if (this.ended) {
this._parser.emit('doneWritingFile');
}
});
});
this._parser.on('end', () => {
this._flushing -= 1;
this.ended = true;
const done = () => {
file.end(() => {
this.emit('file', 'file', file);
this._maybeEnd();
});
};
if (outstandingWrites === 0) {
done();
} else {
this._parser.once('doneWritingFile', done);
}
});
return this;
}
================================================
FILE: src/plugins/querystring.js
================================================
/* eslint-disable no-underscore-dangle */
import QuerystringParser from '../parsers/Querystring.js';
export const querystringType = 'urlencoded';
// the `options` is also available through the `this.options` / `formidable.options`
export default function plugin(formidable, options) {
// the `this` context is always formidable, as the first argument of a plugin
// but this allows us to customize/test each plugin
/* istanbul ignore next */
const self = this || formidable;
if (/^[^;]*urlencoded/i.test(self.headers['content-type'])) {
init.call(self, self, options);
}
return self;
};
// Note that it's a good practice (but it's up to you) to use the `this.options` instead
// of the passed `options` (second) param, because when you decide
// to test the plugin you can pass custom `this` context to it (and so `this.options`)
function init(_self, _opts) {
this.type = querystringType;
const parser = new QuerystringParser(this.options);
parser.on('data', ({ key, value }) => {
this.emit('field', key, value);
});
parser.once('end', () => {
this.ended = true;
this._maybeEnd();
});
this._parser = parser;
return this;
}
================================================
FILE: test/fixture/file/funkyfilename.txt
================================================
I am a text file with a funky name!
================================================
FILE: test/fixture/file/plain.txt
================================================
I am a simple plain text file
================================================
FILE: test/fixture/file/second-plaintext.txt
================================================
Some another plain text file, yeah!
================================================
FILE: test/fixture/http/encoding/beta-sticker-1.png.http
================================================
POST /upload HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
Content-Length: 2483
--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
Content-Disposition: form-data; name="sticker"; filename="beta-sticker-1.png"
Content-Type: image/png
Content-Transfer-Encoding: base64
iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABh5JREFUeNrMmHtIHEcYwGfv5SNwaovxEanEiJKqlYCCTRo1f0SvDeof1legEcE/YttQaNOiaQjYFFtpKaJILZU8SCRUWqlJGpoWepGLTXqUEnzFxCrnK9DEelbvvPOe/WacuY7r7HmGFjrwsbNzt7u//V7zfYvQ/2xI/9K1/NyvMP9PgCTuGmmL6/0ckD9UOGmbIExUsqMkAPHJjv5QwKRtgKioqDlh5+w/7IFeCuLlxCeA2zQ0IcCwh2qoaLH09fUdTElJ2e/1elU+n0/y+9fvPz4+fvfYsWN3YOoBcXPiocLghD4mBYHhQTCErqWlZU9FRcXJqKiowyqVSk/uSEH4o8fjWVlYWDB2d3e3d3R0WGB5jYqLg/NyGgsKxMNgkDB4451NTU3vxcXF1SlBKB0tFsuVxsbGjlu3bj2GJQeIk8K5RVBqBTMxrYRfuHAh9/jx4+ejo6MPS9I6f6hHPOC6rOLi4vyVlZXf7t27Z5c5/iZfkgMxxyUwFy9ezC0tLe3V6XRJ/MOCAYjWwsLCni0oKCh98uSJaWhoyMZFn0/uT2qBqYi/1NbWxjc0NJwPFUYExc/B53R5eXk5ZrN5YH5+3slFn5+D2uBDzG90IJETExOtzGdC9RelNf78wYMH3xQWFn4Ep0sgyyCr1NmJP6kEIa5tbW3dEx8fXxeKRoJpT76OR3p6enllZWUKTCOwNalFAglWDkTCvLq6+uR2YYKZSw4GQVKNfZQCafjkqhKYTBsTE3NY/uYi2Q4MP5KTkw9QGB3VEMv6G/YioqFLly5lazQavfytxobnUW+PWTGisIyNPEL3QYLB4PPIyMi4EydO7JUBbTIZ0RDYOFPkE8t/OdHczCK6Y/qdzP8BfUTW8Tj/uQndvT1F5vOzVvTLz1PwX4cQbt++fekURsNpSNLIw16v1z/HLsRRgecsSnovm8nxs5bvUe+NN1Bz47fkfBaAXj2aA2BWEsM/3hhFX1/5Fe3NTEAfvn8NXTO+tSH68IiNjU2Qw/AmCzg2XCQp+YyhJAu9c+pl9GJ+KmhiEt38bhjpoyJQRtYudA60k3dwD6o4mouKjmSiolcy0ArRqnXz3rT+knwFEShhNKLNlmmFP7Kf8XxuehHpj0QQmLdPGch/ioYyCSAe57pMaHnJgcprctDdwUkRjKi8CUTWhipvbm7uvlJo3zFNoHJDOznPeGEXqn+9EBUf+AQZXvqU+BEG/KCpHz2flYh+ALO9++ZX5L/Mj3gfevjw4ZRoP+PzD/b4HadPn844c+aMkb0F1DqIz9byzBvquXytvr6+7vr16+Ow9CfN2njjdfFAWpo9o2FnNmm12kQMw24gcvSnhbHb7Y+huHsNlhapLNHSxK3idlq287qhhrkKlSByOBzIZrPhGyCn04ncbjfRGAMV5ZlQxvDw8E+yYi1Q3qpleYjUQlNTU5aysrJqgNBhIAwGVSDCkFj48BVFULA1eCl7XV3dx1CKYK3YqKnY7u9Ti2royclJ76FDh1YhxefgsoFpCIOtra0RuGBQwYbRaLzc1dVlpjA2ZiqmKbWsDAmEYU9Pz8Tg4OCNoqKixNTU1BQostDq6iqBcrlcRBiYfEff1KBR+OnpabPBYOikWlnhtOOWm0zUffpnZ2ednZ2dJtCYMTs7+xkA2x0eHk6gsMYwFPYr/EC1Wo2LMEWzWa1WC1QRZ8FUVgpj42ohD3umWqHjRFxf5RkZGVkCNQ9CcTWQn5+flpSUtBOiMKAt7Fek/FSAmpmZMVdVVZ0dGxv7g4PhteMVlbBIofv0sh4Lbmhtb2+/Cbv1eFpaWmJCQsJODMO0hGGgUghAAay9v7//i5KSki9lmmG+4+Jg/MHaIH6f0dCkqaNFFc5VkViam5v319TUNEDdvRubEGsNYHGqsAwMDFxta2u7DdpdpA+3c+LgWiHfVkCiFnpDw0iLqwgqO6BVKoPo00K6WIDsOzE6OrpE395FzeLgxMn5jVe0dYTa26s5jfFg4VR0nAuwNtrFda1rgmToD6VzVWq3eTPyYAxOwwH5gvT2PiWY7X4fUgJTywp1fivyyL6E+Lb6XvQ0X9AkBeeXZED+p/k+9LcAAwAXm3hBLzoZPAAAAABJRU5ErkJggg==
--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/--
================================================
FILE: test/fixture/http/encoding/binaryfile.tar.gz.http
================================================
POST /upload HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
Content-Length: 676
--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
Content-Disposition: form-data; name="file"; filename="binaryfile.tar.gz"
Content-Type: application/x-gzip
Content-Transfer-Encoding: base64
H4sIAGiNIU8AA+3R0W6CMBQGYK59iobLZantRDG73osUOGqnFNJWM2N897UghG1ZdmWWLf93U/jP4bRAq8q92hJ/dY1J7kQEqyyLq8yXYrp2ltkqkTKXYiEykYc++ZTLVcLEvQ40dXReWcYSV1pdnL/v+6n+R11mjKVG1ZQ+s3TT2FpXqjhQ+hjzE1mnGxNLkgu+7tOKWjIVmVKTC6XL9ZaeXj4VQhwKWzL+cI4zwgQuuhkh3mhTad/Hkssh3im3027X54JnQ360R/M19OT8kC7SEN7Ooi2VvrEfznHQRWzl83gxttZKmzGehzPRW/+W8X+3fvL8sFet9sS6m3EIma02071MU3Uf9KHrmV1/+y8DAAAAAAAAAAAAAAAAAAAAAMB/9A6txIuJACgAAA==
--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/--
================================================
FILE: test/fixture/http/encoding/blank.gif.http
================================================
POST /upload HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
Content-Length: 323
--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
Content-Disposition: form-data; name="file"; filename="blank.gif"
Content-Type: image/gif
Content-Transfer-Encoding: base64
R0lGODlhAQABAJH/AP///wAAAMDAwAAAACH5BAEAAAIALAAAAAABAAEAAAICVAEAOw==
--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/--
================================================
FILE: test/fixture/http/encoding/menu_separator.png.http
================================================
POST /upload HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
Content-Length: 1509
--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
Content-Disposition: form-data; name="image"; filename="menu_separator.png"
Content-Type: image/png
Content-Transfer-Encoding: base64
iVBORw0KGgoAAAANSUhEUgAAAAIAAAAYCAIAAABfmbuOAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MDcxODNBNzJERDcyMTFFMUFBOEVFNDQzOTA0MDJDMjQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MDcxODNBNzNERDcyMTFFMUFBOEVFNDQzOTA0MDJDMjQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowNzE4M0E3MERENzIxMUUxQUE4RUU0NDM5MDQwMkMyNCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowNzE4M0E3MURENzIxMUUxQUE4RUU0NDM5MDQwMkMyNCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pmvhbb8AAAAXSURBVHjaYnHk9PON8WJiAIPBSwEEGAAPrgG+VozFWgAAAABJRU5ErkJggg==
--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/--
================================================
FILE: test/fixture/http/encoding/plain.txt.http
================================================
POST /upload HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ
Content-Length: 221
------TLV0SrKD4z1TRxRhAPUvZ
Content-Disposition: form-data; name="file"; filename="plain.txt"
Content-Type: text/plain
Content-Transfer-Encoding: 7bit
I am a plain text file
------TLV0SrKD4z1TRxRhAPUvZ--
================================================
FILE: test/fixture/http/misc/boundary-substring-json.http
================================================
POST /upload HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=uj05Dyqd7Fd5aqAJnK1j9WeJSONmNy5vSGbM1oLf
Content-Length: 211
--uj05Dyqd7Fd5aqAJnK1j9WeJSONmNy5vSGbM1oLf
Content-Disposition: form-data; filename="plain.txt"; name="upload"
Content-Type: text/plain
I am a plain text file
--uj05Dyqd7Fd5aqAJnK1j9WeJSONmNy5vSGbM1oLf--
================================================
FILE: test/fixture/http/misc/empty-multipart.http
================================================
POST /upload HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ
Content-Length: 0
================================================
FILE: test/fixture/http/misc/empty-multipart2.http
================================================
POST /upload HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ
Content-Length: 31
------TLV0SrKD4z1TRxRhAPUvZ--
================================================
FILE: test/fixture/http/misc/empty-urlencoded.http
================================================
POST /upload HTTP/1.1
Host: localhost:8080
Content-Length: 0
Content-Type: application/x-www-form-urlencoded
================================================
FILE: test/fixture/http/misc/empty.http
================================================
POST /upload HTTP/1.1
Host: localhost:8080
Content-Length: 0
================================================
FILE: test/fixture/http/misc/minimal.http
================================================
POST /upload HTTP/1.1
Host: localhost:8080
================================================
FILE: test/fixture/http/no-filename/filename-name.http
================================================
POST /upload HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarytyE4wkKlZ5CQJVTG
Content-Length: 1000
------WebKitFormBoundarytyE4wkKlZ5CQJVTG
Content-Disposition: form-data; filename="plain.txt"; name="upload"
Content-Type: text/plain
I am a plain text file
------WebKitFormBoundarytyE4wkKlZ5CQJVTG--
================================================
FILE: test/fixture/http/no-filename/generic.http
================================================
POST /upload HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarytyE4wkKlZ5CQJVTG
Content-Length: 1000
------WebKitFormBoundarytyE4wkKlZ5CQJVTG
Content-Disposition: form-data; name="upload"; filename=""
Content-Type: text/plain
I am a plain text file
------WebKitFormBoundarytyE4wkKlZ5CQJVTG--
================================================
FILE: test/fixture/http/preamble/crlf.http
================================================
POST /upload HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ
Content-Length: 184
------TLV0SrKD4z1TRxRhAPUvZ
Content-Disposition: form-data; name="upload"; filename="plain.txt"
Content-Type: text/plain
I am a plain text file
------TLV0SrKD4z1TRxRhAPUvZ--
================================================
FILE: test/fixture/http/preamble/preamble.http
================================================
POST /upload HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ
Content-Length: 226
This is a preamble which should be ignored
------TLV0SrKD4z1TRxRhAPUvZ
Content-Disposition: form-data; name="upload"; filename="plain.txt"
Content-Type: text/plain
I am a plain text file
------TLV0SrKD4z1TRxRhAPUvZ--
================================================
FILE: test/fixture/http/special-chars-in-filename/info.md
================================================
- Opera does not allow submitting this file, it shows a warning to the
user that the file could not be found instead. Tested in 9.8, 11.51 on OSX.
Reported to Opera on 08.09.2011 (tracking email DSK-346009@bugs.opera.com).
================================================
FILE: test/fixture/http/special-chars-in-filename/line-separator.http
================================================
POST /upload HTTP/1.1
Host: localhost:8080
Referer: http://localhost:8080/
Content-Length: 383
Origin: http://localhost:8080
User-Agent: Mozilla/5.0
Content-Type: multipart/form-data; boundary=----SEPARATOR
Accept: text/html
------SEPARATOR
Content-Disposition: form-data; name="title"
Weird filename
------SEPARATOR
Content-Disposition: form-data; name="upload"; filename="
.txt"
Content-Type: text/plain
I am a text file with a funky name!
------SEPARATOR--
================================================
FILE: test/fixture/http/special-chars-in-filename/osx-chrome-13.http
================================================
POST /upload HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Referer: http://localhost:8080/
Content-Length: 383
Cache-Control: max-age=0
Origin: http://localhost:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.220 Safari/535.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarytyE4wkKlZ5CQJVTG
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Cookie: jqCookieJar_tablesorter=%7B%22showListTable%22%3A%5B%5B5%2C1%5D%2C%5B1%2C0%5D%5D%7D
------WebKitFormBoundarytyE4wkKlZ5CQJVTG
Content-Disposition: form-data; name="title"
Weird filename
------WebKitFormBoundarytyE4wkKlZ5CQJVTG
Content-Disposition: form-data; name="upload"; filename=": \ ? % * | %22 < > . ? ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt"
Content-Type: text/plain
I am a text file with a funky name!
------WebKitFormBoundarytyE4wkKlZ5CQJVTG--
================================================
FILE: test/fixture/http/special-chars-in-filename/osx-firefox-3.6.http
================================================
POST /upload HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.22) Gecko/20110902 Firefox/3.6.22
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Referer: http://localhost:8080/
Content-Type: multipart/form-data; boundary=---------------------------9849436581144108930470211272
Content-Length: 438
-----------------------------9849436581144108930470211272
Content-Disposition: form-data; name="title"
Weird filename
-----------------------------9849436581144108930470211272
Content-Disposition: form-data; name="upload"; filename=": \ ? % * | " < > . ☃ ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt"
Content-Type: text/plain
I am a text file with a funky name!
-----------------------------9849436581144108930470211272--
================================================
FILE: test/fixture/http/special-chars-in-filename/osx-safari-5.http
================================================
POST /upload HTTP/1.1
Host: localhost:8080
Origin: http://localhost:8080
Content-Length: 383
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryQJZ1gvhvdgfisJPJ
Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Referer: http://localhost:8080/
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: keep-alive
------WebKitFormBoundaryQJZ1gvhvdgfisJPJ
Content-Disposition: form-data; name="title"
Weird filename
------WebKitFormBoundaryQJZ1gvhvdgfisJPJ
Content-Disposition: form-data; name="upload"; filename=": \ ? % * | %22 < > . ? ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt"
Content-Type: text/plain
I am a text file with a funky name!
------WebKitFormBoundaryQJZ1gvhvdgfisJPJ--
================================================
FILE: test/fixture/http/special-chars-in-filename/xp-chrome-12.http
================================================
POST /upload HTTP/1.1
Host: 192.168.56.1:8080
Connection: keep-alive
Referer: http://192.168.56.1:8080/
Content-Length: 344
Cache-Control: max-age=0
Origin: http://192.168.56.1:8080
User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.122 Safari/534.30
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryEvqBNplR3ByrwQPa
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,sdch
Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
------WebKitFormBoundaryEvqBNplR3ByrwQPa
Content-Disposition: form-data; name="title"
Weird filename
------WebKitFormBoundaryEvqBNplR3ByrwQPa
Content-Disposition: form-data; name="upload"; filename=" ? % * | %22 < > . ? ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt"
Content-Type: text/plain
------WebKitFormBoundaryEvqBNplR3ByrwQPa--
================================================
FILE: test/fixture/http/special-chars-in-filename/xp-ie-7.http
================================================
POST /upload HTTP/1.1
Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, */*
Referer: http://192.168.56.1:8080/
Accept-Language: de
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)
Content-Type: multipart/form-data; boundary=---------------------------7db1fe232017c
Accept-Encoding: gzip, deflate
Host: 192.168.56.1:8080
Content-Length: 368
Connection: Keep-Alive
Cache-Control: no-cache
-----------------------------7db1fe232017c
Content-Disposition: form-data; name="title"
Weird filename
-----------------------------7db1fe232017c
Content-Disposition: form-data; name="upload"; filename=" ? % * | " < > . ☃ ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt"
Content-Type: application/octet-stream
-----------------------------7db1fe232017c--
================================================
FILE: test/fixture/http/special-chars-in-filename/xp-ie-8.http
================================================
POST /upload HTTP/1.1
Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, */*
Referer: http://192.168.56.1:8080/
Accept-Language: de
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)
Content-Type: multipart/form-data; boundary=---------------------------7db3a8372017c
Accept-Encoding: gzip, deflate
Host: 192.168.56.1:8080
Content-Length: 368
Connection: Keep-Alive
Cache-Control: no-cache
-----------------------------7db3a8372017c
Content-Disposition: form-data; name="title"
Weird filename
-----------------------------7db3a8372017c
Content-Disposition: form-data; name="upload"; filename=" ? % * | " < > . ☃ ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt"
Content-Type: application/octet-stream
-----------------------------7db3a8372017c--
================================================
FILE: test/fixture/http/special-chars-in-filename/xp-safari-5.http
================================================
POST /upload HTTP/1.1
Host: 192.168.56.1:8080
Referer: http://192.168.56.1:8080/
Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-US
Origin: http://192.168.56.1:8080
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarykmaWSUbu697WN9TM
Content-Length: 344
Connection: keep-alive
------WebKitFormBoundarykmaWSUbu697WN9TM
Content-Disposition: form-data; name="title"
Weird filename
------WebKitFormBoundarykmaWSUbu697WN9TM
Content-Disposition: form-data; name="upload"; filename=" ? % * | %22 < > . ? ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt"
Content-Type: text/plain
------WebKitFormBoundarykmaWSUbu697WN9TM--
================================================
FILE: test/fixture/http/workarounds/missing-hyphens1.http
================================================
POST /upload HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ
Content-Length: 189
------TLV0SrKD4z1TRxRhAPUvZ
Content-Disposition: form-data; name="upload"; filename="plain.txt"
Content-Type: text/plain
I am a simple plain text file
------TLV0SrKD4z1TRxRhAPUvZ
================================================
FILE: test/fixture/http/workarounds/missing-hyphens2.http
================================================
POST /upload HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=----TLVx0SrKD4z1TRxRhAPUvZx
Content-Length: 209
------TLVx0SrKD4z1TRxRhAPUvZx
Content-Disposition: form-data; name="upload"; filename="second-plaintext.txt"
Content-Type: text/plain
Some another plain text file, yeah!
------TLVx0SrKD4z1TRxRhAPUvZx
================================================
FILE: test/fixture/js/encoding.js
================================================
const menu_separator_png_http = [
{
type: 'file',
name: 'image',
originalFilename: 'menu_separator.png',
fixture: 'menu_separator.png',
sha1: 'c845ca3ea794be298f2a1b79769b71939eaf4e54',
},
];
const beta_sticker_1_png_http = [
{
type: 'file',
name: 'sticker',
originalFilename: 'beta-sticker-1.png',
fixture: 'beta-sticker-1.png',
sha1: '6abbcffd12b4ada5a6a084fe9e4584f846331bc4',
},
];
const blank_gif_http = [
{
type: 'file',
name: 'file',
originalFilename: 'blank.gif',
fixture: 'blank.gif',
sha1: 'a1fdee122b95748d81cee426d717c05b5174fe96',
},
];
const binaryfile_tar_gz_http = [
{
type: 'file',
name: 'file',
originalFilename: 'binaryfile.tar.gz',
fixture: 'binaryfile.tar.gz',
sha1: 'cfabe13b348e5e69287d677860880c52a69d2155',
},
];
const plain_txt_http = [
{
type: 'file',
name: 'file',
originalFilename: 'plain.txt',
fixture: 'plain.txt',
sha1: 'b31d07bac24ac32734de88b3687dddb10e976872',
},
];
export {
menu_separator_png_http,
beta_sticker_1_png_http,
blank_gif_http,
binaryfile_tar_gz_http,
plain_txt_http,
};
================================================
FILE: test/fixture/js/misc.js
================================================
const boundary_substring_json = [
{
type: 'file',
name: 'upload',
originalFilename: 'plain.txt',
fixture: 'boundary-substring-json',
},
];
const empty_http = [];
const empty_urlencoded_http = [];
const empty_multipart_http = [];
const empty_multipart2_http = [];
const _minimal_http = [];
export {
boundary_substring_json,
empty_http,
empty_urlencoded_http,
empty_multipart_http,
empty_multipart2_http,
_minimal_http,
};
================================================
FILE: test/fixture/js/no-filename.js
================================================
const generic_http = [
{
type: 'file',
name: 'upload',
originalFilename: '',
fixture: 'generic',
sha1: 'b31d07bac24ac32734de88b3687dddb10e976872',
},
];
const filename_name_http = [
{
type: 'file',
name: 'upload',
originalFilename: 'plain.txt',
fixture: 'filename-name',
sha1: 'a47f7a8a7959f36c3f151ba8b0bd28f2d6b606e2',
},
];
export { generic_http, filename_name_http };
================================================
FILE: test/fixture/js/preamble.js
================================================
const crlf_http = [
{
type: 'file',
name: 'upload',
originalFilename: 'plain.txt',
fixture: 'crlf',
sha1: 'b31d07bac24ac32734de88b3687dddb10e976872',
},
];
const preamble_http = [
{
type: 'file',
name: 'upload',
originalFilename: 'plain.txt',
fixture: 'preamble',
sha1: 'a47f7a8a7959f36c3f151ba8b0bd28f2d6b606e2',
},
];
export { crlf_http, preamble_http };
================================================
FILE: test/fixture/js/special-chars-in-filename.js
================================================
const properFilename = 'funkyfilename.txt';
function expect(originalFilename, fixtureName) {
return [
{
type: 'field',
name: 'title',
originalFilename: properFilename,
fixture: fixtureName,
},
{
type: 'file',
name: 'upload',
originalFilename,
fixture: fixtureName,
},
];
}
const osx_chrome_13_http = expect(' ? % * | " < > . ? ; \' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt', 'osx-chrome-13');
const osx_firefox_3_6_http = expect(' ? % * | " < > . ☃ ; \' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt', 'osx-firefox-3.6');
const xp_ie_7_http = expect(' ? % * | " < > . ☃ ; \' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt', 'xp-ie-7');
const xp_ie_8_http = expect(' ? % * | " < > . ☃ ; \' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt', 'xp-ie-8');
const lineSeparator = expect(null, 'line-separator');
export {
osx_chrome_13_http,
osx_firefox_3_6_http,
// webkit as osx_firefox_3_6_http,
// webkit as osx_safari_5_http,
// webkit as xp_chrome_12_http,
// webkit as xp_safari_5_http,
// xp_ie_7_http,
// xp_ie_8_http,
// lineSeparator,
};
================================================
FILE: test/fixture/js/workarounds.js
================================================
const missing_hyphens1_http = [
{
type: 'file',
name: 'upload',
originalFilename: 'plain.txt',
fixture: 'missing-hyphens1',
sha1: '8c26b82ec9107e99b3486844644e92558efe0c73',
},
];
const missing_hyphens2_http = [
{
type: 'file',
name: 'upload',
originalFilename: 'second-plaintext.txt',
fixture: 'missing-hyphens2',
sha1: '798e39a4a1034232ed26e0aadd67f5d1ff10b966',
},
];
export { missing_hyphens1_http, missing_hyphens2_http };
================================================
FILE: test/fixture/multipart.js
================================================
exports.rfc1867 = {
boundary: 'AaB03x',
raw:
'--AaB03x\r\n' +
'content-disposition: form-data; name="field1"\r\n' +
'\r\n' +
'Joe Blow\r\nalmost tricked you!\r\n' +
'--AaB03x\r\n' +
'content-disposition: form-data; name="pics"; filename="file1.txt"\r\n' +
'Content-Type: text/plain\r\n' +
'\r\n' +
'... contents of file1.txt ...\r\r\n' +
'--AaB03x--\r\n',
parts: [
{
headers: {
'content-disposition': 'form-data; name="field1"',
},
data: 'Joe Blow\r\nalmost tricked you!',
},
{
headers: {
'content-disposition': 'form-data; name="pics"; filename="file1.txt"',
'Content-Type': 'text/plain',
},
data: '... contents of file1.txt ...\r',
},
],
};
exports['noTrailing\r\n'] = {
boundary: 'AaB03x',
raw:
'--AaB03x\r\n' +
'content-disposition: form-data; name="field1"\r\n' +
'\r\n' +
'Joe Blow\r\nalmost tricked you!\r\n' +
'--AaB03x\r\n' +
'content-disposition: form-data; name="pics"; filename="file1.txt"\r\n' +
'Content-Type: text/plain\r\n' +
'\r\n' +
'... contents of file1.txt ...\r\r\n' +
'--AaB03x--',
parts: [
{
headers: {
'content-disposition': 'form-data; name="field1"',
},
data: 'Joe Blow\r\nalmost tricked you!',
},
{
headers: {
'content-disposition': 'form-data; name="pics"; filename="file1.txt"',
'Content-Type': 'text/plain',
},
data: '... contents of file1.txt ...\r',
},
],
};
exports.emptyHeader = {
boundary: 'AaB03x',
raw:
'--AaB03x\r\n' +
'content-disposition: form-data; name="field1"\r\n' +
': foo\r\n' +
'\r\n' +
'Joe Blow\r\nalmost tricked you!\r\n' +
'--AaB03x\r\n' +
'content-disposition: form-data; name="pics"; filename="file1.txt"\r\n' +
'Content-Type: text/plain\r\n' +
'\r\n' +
'... contents of file1.txt ...\r\r\n' +
'--AaB03x--\r\n',
expectError: true,
};
================================================
FILE: test/integration/file-write-stream-handler-option.test.js
================================================
import { existsSync, mkdirSync, createWriteStream, readdirSync, statSync, unlinkSync, createReadStream } from 'node:fs';
import { tmpdir } from 'node:os';
import { createServer, request as _request } from 'node:http';
import path, { join, dirname } from 'node:path';
import url from 'node:url';
import assert, { strictEqual, ok } from 'node:assert';
import formidable from '../../src/index.js';
const __filename = url.fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const PORT = 13533;
const DEFAULT_UPLOAD_DIR = join(
tmpdir(),
'test-store-files-option-default',
);
const CUSTOM_UPLOAD_DIR = join(
tmpdir(),
'test-store-files-option-custom',
);
const CUSTOM_UPLOAD_FILE_PATH = join(CUSTOM_UPLOAD_DIR, 'test-file');
const testFilePath = join(
dirname(__dirname),
'fixture',
'file',
'binaryfile.tar.gz',
);
const createDirs = (dirs) => {
dirs.forEach((dir) => {
if (!existsSync(dir)) {
mkdirSync(dir);
}
});
};
test('file write stream handler', (done) => {
const server = createServer((req, res) => {
createDirs([DEFAULT_UPLOAD_DIR, CUSTOM_UPLOAD_DIR]);
const form = formidable({
uploadDir: DEFAULT_UPLOAD_DIR,
fileWriteStreamHandler: () =>
createWriteStream(CUSTOM_UPLOAD_FILE_PATH),
});
form.parse(req, (err, fields, files) => {
strictEqual(Object.keys(files).length, 1);
const file = files.file[0];
strictEqual(file.size, 301);
strictEqual(typeof file.filepath, 'string');
const dirFiles = readdirSync(DEFAULT_UPLOAD_DIR);
ok(dirFiles.length === 0);
const uploadedFileStats = statSync(CUSTOM_UPLOAD_FILE_PATH);
ok(uploadedFileStats.size === file.size);
unlinkSync(CUSTOM_UPLOAD_FILE_PATH);
res.end();
server.close();
done();
});
});
server.listen(PORT, (err) => {
assert(!err, 'should not have error, but be falsey');
const request = _request({
port: PORT,
method: 'POST',
headers: {
'Content-Type': 'application/octet-stream',
},
});
createReadStream(testFilePath).pipe(request);
});
});
================================================
FILE: test/integration/fixtures.test.js
================================================
/* eslint-disable global-require */
/* eslint-disable import/no-dynamic-require */
import { createReadStream } from 'node:fs';
import { createConnection } from 'node:net';
import { join } from 'node:path';
import { createServer } from 'node:http';
import { strictEqual } from 'node:assert';
import formidable from '../../src/index.js';
const PORT = 13534;
const CWD = process.cwd();
const FIXTURES_HTTP = join(CWD, 'test', 'fixture', 'http');
const UPLOAD_DIR = join(CWD, 'test', 'tmp');
import * as encoding from "../fixture/js/encoding.js";
import * as misc from "../fixture/js/misc.js";
import * as noFilename from "../fixture/js/no-filename.js";
import * as preamble from "../fixture/js/preamble.js";
import * as workarounds from "../fixture/js/workarounds.js";
import * as specialCharsInFilename from "../fixture/js/special-chars-in-filename.js";
const fixtures= {
encoding,
misc,
[`no-filename`]: noFilename,
preamble,
[`special-chars-in-filename`]: specialCharsInFilename,
// workarounds, // todo uncomment this and make it work
};
test('fixtures', (done) => {
const server = createServer();
server.listen(PORT, findFixtures);
function findFixtures() {
const results = Object.entries(fixtures).map(([fixtureGroup, fixture]) => {
return Object.entries(fixture).map(([k, v]) => {
return v.map(details => {
return {
fixture: v,
name: `${fixtureGroup}/${details.fixture}.http`
};
});
});
}).flat(Infinity);
testNext(results);
}
function testNext(results) {
const fixtureWithName = results.shift();
if (!fixtureWithName) {
server.close();
done();
return;
}
const fixtureName = fixtureWithName.name;
const fixture = fixtureWithName.fixture;
uploadFixture(fixtureName, (err, parts) => {
try {
if (err) {
err.fixtureName = fixtureName;
throw err;
}
fixture.forEach((expectedPart, i) => {
const parsedPart = parts[i];
strictEqual(parsedPart.type, expectedPart.type);
strictEqual(parsedPart.name, expectedPart.name);
if (parsedPart.type === 'file') {
const file = parsedPart.value;
strictEqual(file.originalFilename, expectedPart.originalFilename,
`${JSON.stringify([expectedPart, file])}`);
if (expectedPart.sha1) {
strictEqual(
file.hash,
expectedPart.sha1,
`SHA1 error ${file.originalFilename} on ${file.filepath} ${JSON.stringify([expectedPart, file])}`,
);
}
}
});
} catch (e) {
server.close();
done(e);
throw e;
}
testNext(results);
});
}
function uploadFixture(fixtureName, cb) {
server.once('request', (req, res) => {
const form = formidable({
uploadDir: UPLOAD_DIR,
hashAlgorithm: 'sha1',
});
function callback(...args) {
const realCallback = cb;
// eslint-disable-next-line no-param-reassign
cb = function callbackFn() {};
realCallback(...args);
}
const parts = [];
form
.on('error', callback)
.on('fileBegin', (name, value) => {
parts.push({ type: 'file', name, value });
})
.on('field', (name, value) => {
parts.push({ type: 'field', name, value });
})
.on('end', () => {
res.end();
callback(null, parts);
});
form.parse(req);
});
const socket = createConnection(PORT);
const fixturePath = join(FIXTURES_HTTP, fixtureName);
const file = createReadStream(fixturePath);
file.pipe(socket, { end: false });
socket.on('data', () => {
socket.end();
});
}
});
================================================
FILE: test/integration/json.test.js
================================================
import { createServer, request as _request } from 'node:http';
import assert, { deepStrictEqual } from 'node:assert';
import formidable from '../../src/index.js';
const testData = {
numbers: [1, 2, 3, 4, 5],
nested: { key: 'val' },
};
const PORT = 13535;
test('json', (done) => {
const server = createServer((req, res) => {
const form = formidable({ });
form.parse(req, (err, fields) => {
deepStrictEqual(fields, {
numbers: [1, 2, 3, 4, 5],
nested: { key: 'val' },
});
res.end();
server.close();
done();
});
});
server.listen(PORT, (err) => {
assert(!err, 'should not have error, but be falsey');
const request = _request({
port: PORT,
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
});
request.write(JSON.stringify(testData));
request.end();
});
});
================================================
FILE: test/integration/octet-stream.test.js
================================================
import { readFileSync, createReadStream } from 'node:fs';
import { createServer, request as _request } from 'node:http';
import path, { join, dirname } from 'node:path';
import url from 'node:url';
import assert, { strictEqual, deepStrictEqual } from 'node:assert';
import formidable from '../../src/index.js';
const __filename = url.fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const PORT = 13536;
const testFilePath = join(
dirname(__dirname),
'fixture',
'file',
'binaryfile.tar.gz',
);
test('octet stream', (done) => {
const server = createServer((req, res) => {
const form = formidable();
form.parse(req, (err, fields, files) => {
strictEqual(Object.keys(files).length, 1);
const file = files.file[0];
strictEqual(file.size, 301);
const uploaded = readFileSync(file.filepath);
const original = readFileSync(testFilePath);
deepStrictEqual(uploaded, original);
res.end();
server.close();
done();
});
});
server.listen(PORT, (err) => {
assert(!err, 'should not have error, but be falsey');
const request = _request({
port: PORT,
method: 'POST',
headers: {
'Content-Type': 'application/octet-stream',
},
});
createReadStream(testFilePath).pipe(request);
});
});
================================================
FILE: test/integration/store-files-option.test.js
================================================
import { existsSync, mkdirSync, WriteStream, statSync, unlinkSync, createReadStream } from 'node:fs';
import { tmpdir } from 'node:os';
import { createServer, request as _request } from 'http';
import assert, { strictEqual, ok } from 'node:assert';
import path, { join, dirname } from 'node:path';
import url from 'node:url';
import formidable from '../../src/index.js';
const __filename = url.fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const PORT = 13537;
const DEFAULT_UPLOAD_DIR = join(
tmpdir(),
'test-store-files-option-default',
);
const CUSTOM_UPLOAD_FILE_PATH = join(DEFAULT_UPLOAD_DIR, 'test-file');
const testFilePath = join(
dirname(__dirname),
'fixture',
'file',
'binaryfile.tar.gz',
);
test('store files option', (done) => {
const server = createServer((req, res) => {
if (!existsSync(DEFAULT_UPLOAD_DIR)) {
mkdirSync(DEFAULT_UPLOAD_DIR);
}
const form = formidable({
uploadDir: DEFAULT_UPLOAD_DIR,
fileWriteStreamHandler: () => new WriteStream(CUSTOM_UPLOAD_FILE_PATH),
});
form.parse(req, (err, fields, files) => {
strictEqual(Object.keys(files).length, 1);
const file = files.file[0];
strictEqual(file.size, 301);
strictEqual(typeof file.filepath, 'string');
const uploadedFileStats = statSync(CUSTOM_UPLOAD_FILE_PATH);
ok(uploadedFileStats.size === file.size);
unlinkSync(CUSTOM_UPLOAD_FILE_PATH);
res.end();
server.close();
done();
});
});
server.listen(PORT, (err) => {
assert(!err, 'should not have error, but be falsey');
const request = _request({
port: PORT,
method: 'POST',
headers: {
'Content-Type': 'application/octet-stream',
},
});
createReadStream(testFilePath).pipe(request);
});
});
================================================
FILE: test/standalone/connection-aborted.test.js
================================================
import assert from 'node:assert';
import { createServer } from 'node:http';
import { connect } from 'node:net';
import formidable from '../../src/index.js';
let server;
let port = 13540;
beforeEach(() => {
server = createServer();
port += 1;
});
afterEach(() => {
return new Promise((resolve) => {
if (server.listening) {
server.close(() => resolve());
} else {
resolve();
}
});
});
test('connection aborted', (done) => {
server.on('request', (req) => {
const form = formidable();
let abortedReceived = false;
form.on('aborted', () => {
abortedReceived = true;
});
form.on('error', () => {
assert(abortedReceived, 'Error event should follow aborted');
});
form.on('end', () => {
throw new Error('Unexpected "end" event');
});
form.parse(req, () => {
assert(
abortedReceived,
'from .parse() callback: Error event should follow aborted',
);
done();
});
});
server.listen(port, 'localhost', () => {
const client = connect(port);
client.write(
'POST / HTTP/1.1\r\n' +
'Host: localhost\r\n' +
'Content-Length: 70\r\n' +
'Content-Type: multipart/form-data; boundary=foo\r\n\r\n',
);
client.end();
});
});
================================================
FILE: test/standalone/content-transfer-encoding.test.js
================================================
import { join } from 'node:path';
import { createServer, request } from 'node:http';
import { strictEqual } from 'node:assert';
import formidable from '../../src/index.js';
const UPLOAD_DIR = join(process.cwd(), 'test', 'tmp');
// OS choosing port
const PORT = 13530;
test('content transfer encoding', (done) => {
const server = createServer(async (req, res) => {
const form = formidable({
uploadDir: UPLOAD_DIR
});
form.on('end', () => {
throw new Error('Unexpected "end" event');
});
form.on('error', (e) => {
res.writeHead(500);
res.end(e.message);
});
try {
await form.parse(req);
} catch (formidableError) {
}
});
server.listen(PORT, () => {
const chosenPort = server.address().port;
const body =
'--foo\r\n' +
'Content-Disposition: form-data; name="file1"; filename="file1"\r\n' +
'Content-Type: application/octet-stream\r\n' +
'\r\nThis is the first file\r\n' +
'--foo\r\n' +
'Content-Type: application/octet-stream\r\n' +
'Content-Disposition: form-data; name="file2"; filename="file2"\r\n' +
'Content-Transfer-Encoding: unknown\r\n' +
'\r\nThis is the second file\r\n' +
'--foo--\r\n';
const req = request({
method: 'POST',
port: chosenPort,
headers: {
'Content-Length': body.length,
'Content-Type': 'multipart/form-data; boundary=foo',
},
});
req.on('response', (res) => {
strictEqual(res.statusCode, 500);
res.on('data', () => {});
res.on('end', () => {
server.close();
done();
});
});
req.end(body);
});
});
================================================
FILE: test/standalone/issue-46.test.js
================================================
import { createServer, request } from "node:http";
import { ok, strictEqual } from "node:assert";
import { Buffer } from 'node:buffer';
import formidable from "../../src/index.js";
// OS choosing port
const PORT = 13531;
const type = "multipart/related; boundary=a7a65b99-8a61-4e2c-b149-f73a3b35f923"
const body = "LS1hN2E2NWI5OS04YTYxLTRlMmMtYjE0OS1mNzNhM2IzNWY5MjMNCmNvbnRlbnQtZGlzcG9zaXRpb246IGZvcm0tZGF0YTsgbmFtZT0iZm9vIg0KDQpiYXJyeQ0KLS1hN2E2NWI5OS04YTYxLTRlMmMtYjE0OS1mNzNhM2IzNWY5MjMtLQ";
const buffer = Buffer.from(body, 'base64url');
test("issue 46", (done) => {
const server = createServer(async (req, res) => {
// Parse form and write results to response.
const form = formidable();
const [fields] = await form.parse(req);
ok(fields.foo, 'should have fields.foo === barry');
strictEqual(fields.foo[0], 'barry');
res.end();
server.close(() => {
done();
});
});
server.listen(PORT, () => {
const chosenPort = server.address().port;
const url = `http://localhost:${chosenPort}`;
const req = request(url, {
method: "POST",
headers: {
"Content-Type": type,
"Content-Length": buffer.byteLength
}
});
req.write(buffer);
req.end();
});
});
================================================
FILE: test/standalone/keep-alive-error.test.js
================================================
/* eslint-disable max-nested-callbacks */
import assert from 'node:assert/strict';
import { createServer } from 'node:http';
import { createConnection } from 'node:net';
import formidable from '../../src/index.js';
let server;
let port = 13539;
let ok = 0;
let errors = 0;
beforeEach(() => {
server = createServer();
ok = 0;
errors = 0;
port += 1;
});
afterEach(() => {
return new Promise((resolve) => {
if (server.listening) {
server.close(() => resolve());
} else {
resolve();
}
});
});
test('keep alive error', (done) => {
server.on('request', async (req, res) => {
const form = formidable();
form.on('error', () => {
errors += 1;
res.writeHead(500);
res.end();
});
form.on('end', () => {
ok += 1;
res.writeHead(200);
res.end();
});
try {
await form.parse(req);
// for client two
assert.strictEqual(ok, 1, `should "ok" count === 1, has: ${ok}`);
done();
} catch (formidableError) {
assert.strictEqual(errors, 1, `should "errors" === 1, has: ${errors}`);
const clientTwo = createConnection(port);
// correct post upload (with hyphens)
clientTwo.write(
'POST /upload-test HTTP/1.1\r\n' +
'Host: localhost\r\n' +
'Connection: keep-alive\r\n' +
'Content-Type: multipart/form-data; boundary=----aaa\r\n' +
'Content-Length: 13\r\n\r\n' +
'------aaa--\r\n',
);
clientTwo.end();
}
});
server.listen(port, () => {
const client = createConnection(port);
// first send malformed (boundary / hyphens) post upload
client.write(
'POST /upload-test HTTP/1.1\r\n' +
'Host: localhost\r\n' +
'Connection: keep-alive\r\n' +
'Content-Type: multipart/form-data; boundary=----aaa\r\n' +
'Content-Length: 10011\r\n\r\n' +
'------XXX\n\r',
);
setTimeout(() => {
const buf = Buffer.alloc(10000);
buf.fill('a');
client.write(buf);
client.end();
}, 150);
});
});
================================================
FILE: test/tools/base64.html
================================================
Convert a file to a base64 request
Don't forget to save the output with windows (CRLF) line endings!
================================================
FILE: test/unit/custom-plugins.test.js
================================================
/* eslint-disable no-underscore-dangle */
import { join } from 'node:path';
import Koa from 'koa';
import request from 'supertest';
import { formidable, json, octetstream, multipart, errors } from '../../src/index.js';
function createServer(options, handler) {
const app = new Koa();
app.use(async (ctx, next) => {
const form = formidable(options);
await handler(ctx, form);
await next();
});
return app;
}
function fromFixtures(...args) {
return join(process.cwd(), 'test', 'fixture', ...args);
}
// function makeRequest(server, options) {
// server.listen(0, () => {
// const chosenPort = server.address().port;
// const url = `http://localhost:${chosenPort}`;
// const method = 'POST';
// const opts = {
// ...options,
// port: chosenPort,
// url,
// method,
// };
// return http.request(opts);
// });
// }
// function onDone({ server, form, req, res }) {
// form.parse(req, (err, fields) => {
// assert.strictEqual(fields.qux, 'zaz');
// setTimeout(() => {
// res.end();
// server.close();
// }, 200);
// });
// }
// ! tests
test('should call 3 custom and 1 builtin plugins, when .parse() is called', async () => {
const server = createServer({ enabledPlugins: [json] }, (ctx, form) => {
form.on('plugin', () => {
ctx.__pluginsCount = ctx.__pluginsCount || 0;
ctx.__pluginsCount += 1;
});
form.on('end', () => {
ctx.__ends = 1;
expect(ctx.__customPlugin1).toBe(111);
expect(ctx.__customPlugin2).toBe(222);
expect(ctx.__customPlugin3).toBe(333);
ctx.__ends = 2;
const len = form._plugins.length;
expect(len).toBe(4);
});
form.use(() => {
ctx.__customPlugin1 = 111;
});
form.use(() => {
ctx.__customPlugin2 = 222;
});
form.use(() => {
ctx.__customPlugin3 = 333;
});
form.parse(ctx.req, (err, fields) => {
expect(fields.qux).toBe('zaz');
expect(fields.a).toBe('bbb');
expect(ctx.__pluginsCount).toBe(4);
});
});
await new Promise((resolve, reject) => {
request(server.callback())
.post('/')
.type('application/json')
.send({ qux: 'zaz', a: 'bbb' })
.end((err) => (err ? reject(err) : resolve()));
});
});
test('.parse throw error when some plugin fail', async () => {
const server = createServer(
{ enabledPlugins: [octetstream, json] },
async (ctx, form) => {
// const failedIsOkay = false;
// ! not emitted?
// form.on('file', () => {
// ctx.__onFileCalled = ctx.__onFileCalled || 0;
// ctx.__onFileCalled += 1;
// });
form.on('plugin', () => {
ctx.__pluginsCount = ctx.__pluginsCount || 0;
ctx.__pluginsCount += 1;
});
form.once('error', () => {
throw new Error('error event should not be fired when plugin throw');
});
form.use(() => {
throw new Error('custom plugin err');
});
let res = null;
try {
await form.parse(ctx.req);
} catch (err) {
expect(err.code).toBe(errors.pluginFailed);
expect(err.httpCode).toBe(500);
expect(form._plugins.length).toBe(3);
expect(ctx.__pluginsCount).toBe(2);
expect(ctx.__pluginsResults).toBe(undefined);
res = err;
}
if (!res) {
throw new Error(
'^ .parse should throw & be caught with the try/catch ^',
);
}
},
);
return new Promise((resolve, reject) => {
request(server.callback())
.post('/')
.type('application/octet-stream')
.attach('bin', fromFixtures('file', 'binaryfile.tar.gz'))
.end((err) => (err ? reject(err) : resolve()));
});
});
test('multipart plugin fire `error` event when malformed boundary', async () => {
const server = createServer(
{ enabledPlugins: [json, multipart] },
(ctx, form) => {
let failedIsOkay = false;
form.once('error', (err) => {
expect(form._plugins.length).toBe(2);
expect(err).toBeTruthy();
expect(err.message).toMatch(/bad content-type header/);
expect(err.message).toMatch(/no multipart boundary/);
failedIsOkay = true;
});
// Should never be called when `error`
form.on('end', () => {
throw new Error('should not fire `end` event when error');
});
form.parse(ctx.req, (err) => {
expect(err).toBeTruthy();
expect(failedIsOkay).toBe(true);
});
},
);
// 'Content-Length': 1111111,
// 'content-Disposition': 'form-data; bouZndary=',
// 'Content-Type': 'multipart/form-data; bouZndary=',
await new Promise((resolve, reject) => {
request(server.callback())
.post('/')
.type('multipart/form-data')
.set('Content-Length', 11111111)
.set('Content-Disposition', 'form-data; bouZndary=')
.set('Content-Type', 'multipart/form-data; bouZndary=')
.end((err) => (err ? reject(err) : resolve()));
});
});
test('formidable() throw if not at least 1 built-in plugin in options.enabledPlugins', () => {
try {
formidable({ enabledPlugins: [] });
} catch (err) {
expect(err).toBeTruthy();
expect(err.message).toMatch(/expect at least 1 enabled builtin/);
}
});
================================================
FILE: test/unit/formidable.test.js
================================================
/* eslint-disable max-statements */
/* eslint-disable no-underscore-dangle */
import {jest} from '@jest/globals';
import Stream from 'node:stream';
import http from 'node:http';
import path from 'node:path';
import formidable from '../../src/index.js';
import * as mod from '../../src/index.js';
function requestStub() {
return Object.assign(new Stream (), {
pause() {},
resume() {},
});
}
function getForm(name, opts) {
return name === 'formidable' ? formidable(opts) : new mod[name](opts);
}
function makeHeader(originalFilename) {
return `Content-Disposition: form-data; name="upload"; filename="${originalFilename}"`;
}
['IncomingForm', 'Formidable', 'formidable'].forEach((name) => {
test(`${name}#_getFileName with regular characters`, () => {
const originalFilename = 'foo.txt';
const form = getForm(name);
expect(form._getFileName(makeHeader(originalFilename))).toBe('foo.txt');
});
test(`${name}#_getFileName with unescaped quote`, () => {
const originalFilename = 'my".txt';
const form = getForm(name);
expect(form._getFileName(makeHeader(originalFilename))).toBe('my".txt');
});
test(`${name}#_getFileName with escaped quote`, () => {
const originalFilename = 'my%22.txt';
const form = getForm(name);
expect(form._getFileName(makeHeader(originalFilename))).toBe('my".txt');
});
test(`${name}#_getFileName with bad quote and additional sub-header`, () => {
const originalFilename = 'my".txt';
const form = getForm(name);
const header = `${makeHeader(originalFilename)}; foo="bar"`;
expect(form._getFileName(header)).toBe(originalFilename);
});
test(`${name}#_getFileName with semicolon`, () => {
const originalFilename = 'my;.txt';
const form = getForm(name);
expect(form._getFileName(makeHeader(originalFilename))).toBe('my;.txt');
});
test(`${name}#_getFileName with utf8 character`, () => {
const originalFilename = 'my☃.txt';
const form = getForm(name);
expect(form._getFileName(makeHeader(originalFilename))).toBe('my☃.txt');
});
test(`${name}#_getNewName strips harmful characters from extension when keepExtensions`, () => {
const form = getForm(name, { keepExtensions: true });
const getBasename = (part) => path.basename(form._getNewName(part));
// tests below assume baseline hexoid 25 chars + a few more for the extension
let basename = getBasename('fine.jpg?foo=bar');
expect(basename).toHaveLength(29);
let ext = path.extname(basename);
expect(ext).toBe('.jpg');
basename = getBasename('fine-no-ext?foo=qux');
expect(basename).toHaveLength(25);
ext = path.extname(basename);
expect(ext).toBe('');
basename = getBasename({ originalFilename: 'super.cr2+dsad' });
expect(basename).toHaveLength(29);
ext = path.extname(basename);
expect(ext).toBe('.cr2');
basename = getBasename({ originalFilename: 'super.gz' });
expect(basename).toHaveLength(28);
ext = path.extname(basename);
expect(ext).toBe('.gz');
basename = getBasename('file.aAa');
expect(basename).toHaveLength(29);
ext = path.extname(basename);
expect(ext).toBe('.aAa');
basename = getBasename('file#!@#koh.QxZs?sa=1');
expect(basename).toHaveLength(30);
ext = path.extname(basename);
expect(ext).toBe('.QxZs');
basename = getBasename('test.pdf.jqlnn .png');
expect(basename).toHaveLength(35);
ext = path.extname(basename);
expect(ext).toBe('.jqlnn');
basename = getBasename('test. {
const form = getForm(name, { });
const req = new http.ClientRequest();
req.headers = {
'content-length': '8',
'content-type': 'multipart/form-data; boundary=----TLVx',
};
form.parse(req, (error, fields) => {
expect(Array.isArray(fields.a)).toBe(true);
expect(fields.a[0]).toBe('1');
expect(fields.a[1]).toBe('2');
});
form.emit('field', 'a', '1');
form.emit('field', 'a', '2');
form.emit('end');
});
test(`${name}#_Nested array parameters support`, () => {
const form = getForm(name, { });
const req = new http.ClientRequest();
req.headers = {
'content-length': '8',
'content-type': 'multipart/form-data; boundary=----TLVx',
};
form.parse(req, (error, fields) => {
expect(Array.isArray(fields[`a[0]`])).toBe(true);
expect(fields[`a[0]`][0]).toBe('a');
expect(fields[`a[0]`][1]).toBe('b');
expect(fields[`a[1]`][0]).toBe('c');
});
form.emit('field', 'a[0]', 'a');
form.emit('field', 'a[0]', 'b');
form.emit('field', 'a[1]', 'c');
form.emit('end');
});
test(`${name}#_Object parameters support`, () => {
const form = getForm(name, { });
const req = new http.ClientRequest();
req.headers = {
'content-length': '8',
'content-type': 'multipart/form-data; boundary=----TLVx',
};
form.parse(req, (error, fields) => {
expect(fields[`a[x]`][0]).toBe('1');
expect(fields[`a[y]`][0]).toBe('2');
});
form.emit('field', 'a[x]', '1');
form.emit('field', 'a[y]', '2');
form.emit('end');
});
xtest(`${name}#_Nested object parameters support`, () => {
const form = getForm(name, { });
const req = new http.ClientRequest();
req.headers = {
'content-length': '8',
'content-type': 'multipart/form-data; boundary=----TLVx',
};
form.parse(req, (error, fields) => {
expect(fields.a.l1.k1).toBe('2');
expect(fields.a.l1.k2).toBe('3');
expect(fields.a.l2.k3).toBe('5');
});
form.emit('field', 'a[l1][k1]', '2');
form.emit('field', 'a[l1][k2]', '3');
form.emit('field', 'a[l2][k3]', '5');
form.emit('end');
});
describe(`${name}#_onPart`, () => {
describe('when not allow empty files', () => {
describe('when file is empty', () => {
test('emits error when part is received', (done) => {
const form = getForm(name, {
allowEmptyFiles: false,
});
form.req = requestStub();
const part = new Stream();
part.mimetype = 'text/plain';
// eslint-disable-next-line max-nested-callbacks
form.on('error', (error) => {
expect(error.message).toBe(
'options.allowEmptyFiles is false, file size should be greater than 0',
);
done();
});
form.onPart(part).then (function () {
part.emit('end');
form.emit('end');
});
});
});
describe('when file is not empty', () => {
test('not emits error when part is received', () => {
const form = getForm(name, {
allowEmptyFiles: false,
});
const formEmitSpy = jest.spyOn(form, 'emit');
const part = new Stream();
part.mimetype = 'text/plain';
form.onPart(part);
part.emit('data', Buffer.alloc(1));
part.emit('end');
form.emit('end');
expect(formEmitSpy).not.toBeCalledWith('error');
});
});
});
describe('when allow empty files', () => {
test('not emits error when part is received', () => {
const form = getForm(name, { });
const formEmitSpy = jest.spyOn(form, 'emit');
const part = new Stream();
part.mimetype = 'text/plain';
form.onPart(part);
part.emit('end');
form.emit('end');
expect(formEmitSpy).not.toBeCalledWith('error');
});
});
describe('when file uploaded size is inferior than minFileSize option', () => {
test('emits error when part is received', (done) => {
const form = getForm(name, { minFileSize: 5 });
const part = new Stream();
const req = requestStub();
part.mimetype = 'text/plain';
form.on('error', (error) => {
expect(error.message).toBe(
'options.minFileSize (5 bytes) inferior, received 4 bytes of file data',
);
done();
});
form.req = req;
form.onPart(part).then(function () {
part.emit('data', Buffer.alloc(4));
part.emit('end');
form.emit('end');
});
});
});
describe('when file uploaded size is superior than minFileSize option', () => {
test('not emits error when part is received', () => {
const form = getForm(name, { minFileSize: 10 });
const formEmitSpy = jest.spyOn(form, 'emit');
const part = new Stream();
part.mimetype = 'text/plain';
form.onPart(part);
part.emit('data', Buffer.alloc(11));
part.emit('end');
form.emit('end');
expect(formEmitSpy).not.toBeCalledWith('error');
});
});
describe('when there are more fields than maxFields', () => {
test('emits error', (done) => {
const form = getForm(name, {
maxFields: 1,
});
form.on('error', (error) => {
expect(error.message.includes('maxFields')).toBe(true);
done();
});
form.emit('field', 'a', '1');
form.emit('field', 'b', '2');
form.emit('end');
});
});
});
test(`${name}: maxFiles exceeded emits error`, (done) => {
const form = getForm(name, { maxFiles: 1 });
form.req = requestStub();
form.on('error', (error) => {
expect(error.message.includes('maxFiles')).toBe(true);
done();
});
const part1 = new Stream();
part1.mimetype = 'text/plain';
const part2 = new Stream();
part2.mimetype = 'text/plain';
form.onPart(part1).then(() => {
part1.emit('data', Buffer.alloc(1));
part1.emit('end');
form.onPart(part2);
});
});
test(`${name}: maxFiles defaults to 1000`, () => {
const form = getForm(name);
expect(form.options.maxFiles).toBe(1000);
});
// test(`${name}: use custom options.originalFilename instead of form._uploadPath`, () => {
// const form = getForm(name, {
// originalFilename: (_) => path.join(__dirname, 'sasa'),
// });
// });
test(`${name}#_joinDirectoryName blocks standard directory traversal`, () => {
const form = getForm(name, { uploadDir: '/tmp/uploads' });
const result = form._joinDirectoryName('../../../etc/passwd');
expect(result).toBe(path.join('/tmp/uploads', 'invalid-name'));
});
test(`${name}#_joinDirectoryName blocks sibling directory prefix collision`, () => {
const form = getForm(name, { uploadDir: '/tmp/uploads' });
const result = form._joinDirectoryName('../uploads-evil/payload.sh');
expect(result).toBe(path.join('/tmp/uploads', 'invalid-name'));
});
test(`${name}#_joinDirectoryName allows subdirectories within uploadDir`, () => {
const form = getForm(name, { uploadDir: '/tmp/uploads' });
const result = form._joinDirectoryName('images/photo.jpg');
expect(result).toBe(path.resolve('/tmp/uploads', 'images/photo.jpg'));
});
test(`${name}#_joinDirectoryName blocks name resolving to uploadDir itself`, () => {
const form = getForm(name, { uploadDir: '/tmp/uploads' });
const result = form._joinDirectoryName('.');
expect(result).toBe(path.join('/tmp/uploads', 'invalid-name'));
});
});
================================================
FILE: test/unit/multipart-parser.test.js
================================================
import { MultipartParser } from '../../src/index.js';
test('on constructor', () => {
const parser = new MultipartParser();
expect(parser.boundary).toBeNull();
expect(parser.state).toBe(0);
expect(parser.flags).toBe(0);
expect(parser.boundaryChars).toBeNull();
expect(parser.index).toBeNull();
expect(parser.lookbehind).toBeNull();
expect(parser.constructor.name).toBe('MultipartParser');
});
test('initWithBoundary', () => {
const boundary = 'abc';
const parser = new MultipartParser();
parser.initWithBoundary(boundary);
expect(Array.prototype.slice.call(parser.boundary)).toMatchObject([
13,
10,
45,
45,
97,
98,
99,
]);
expect(parser.state).toBe(MultipartParser.STATES.START);
expect(parser.boundaryChars).toMatchObject({
10: true,
13: true,
45: true,
97: true,
98: true,
99: true,
});
});
test('initWithBoundary failing', () => {
const parser = new MultipartParser();
const boundary = 'abc';
const buffer = Buffer.alloc(5);
parser.initWithBoundary(boundary);
buffer.write('--ad', 0);
expect(parser.bufferLength).toBe(0);
parser.write(buffer);
expect(parser.bufferLength).toBe(5);
});
test('on .end() throwing', () => {
const parser = new MultipartParser();
parser.once('error', () => {});
const res = parser.end();
expect(res.state).toBe(0);
// expect(() => { parser.end() }).toThrow(/MultipartParser/);
// expect(() => { parser.end() }).toThrow(/stream ended unexpectedly/);
// expect(() => { parser.end() }).toThrow(parser.explain());
});
test('on .end() successful', () => {
const parser = new MultipartParser();
parser.state = MultipartParser.STATES.END;
const res = parser.end();
expect(res.state).toBe(12);
});
================================================
FILE: test/unit/persistent-file.disabled-test.js
================================================
import {jest} from '@jest/globals';
import fs from 'node:fs';
import PersistentFile from '../../src/PersistentFile.js';
const mockFs = fs;
const now = new Date();
const file = new PersistentFile({
size: 1024,
filepath: '/tmp/cat.png',
name: 'cat.png',
type: 'image/png',
lastModifiedDate: now,
originalFilename: 'cat.png',
newFilename: 'dff1d2eaab9752165764dcd00',
mimetype: 'image/png',
});
const mockFn = jest.fn();
jest.mock('fs', () => {
return {
...mockFs,
unlink: mockFn,
};
});
describe('PersistentFile', () => {
test('toJSON()', () => {
const obj = file.toJSON();
const len = Object.keys(obj).length;
expect(obj.filepath).toBe('/tmp/cat.png');
expect(obj.mimetype).toBe('image/png');
expect(obj.originalFilename).toBe('cat.png');
});
test('toString()', () => {
const result = file.toString();
expect(result).toBe('PersistentFile: dff1d2eaab9752165764dcd00, Original: cat.png, Path: /tmp/cat.png')
});
test('destroy()', () => {
file.open();
file.destroy();
// eslint-disable-next-line global-require
expect(mockFn).toBeCalled();
});
});
================================================
FILE: test/unit/querystring-parser.test.js
================================================
import { QuerystringParser } from '../../src/index.js';
test('on constructor', () => {
const parser = new QuerystringParser();
expect(parser.constructor.name).toBe('QuerystringParser');
});
// ! skip
// test(function end =>
// const FIELDS = { a: ['b', { c: 'd' }], e: 'f' };
// gently.expect(GENTLY.hijacked.querystring, 'parse', (str) => {
// assert.equal(str, parser.buffer);
// return FIELDS;
// });
// gently.expect(parser, 'onField', Object.keys(FIELDS).length, (key, val) => {
// assert.deepEqual(FIELDS[key], val);
// });
// gently.expect(parser, 'onEnd');
// parser.buffer = 'my buffer';
// parser.end();
// assert.equal(parser.buffer, '');
// });
================================================
FILE: test/unit/volatile-file.test.js
================================================
import {jest} from '@jest/globals';
import VolatileFile from '../../src/VolatileFile.js';
describe('VolatileFile', () => {
let file;
let writeStreamInstanceMock;
let writeStreamMock;
beforeEach(() => {
writeStreamInstanceMock = {
on: jest.fn(),
destroy: jest.fn(),
end: jest.fn(),
write: jest.fn(),
};
writeStreamMock = jest.fn(() => writeStreamInstanceMock);
file = new VolatileFile({
xname: 'cat.png',
originalFilename: 'cat.png',
mimetype: 'image/png',
createFileWriteStream: writeStreamMock,
});
file.open();
});
test('open()', (done) => {
const error = new Error('test');
file.on('error', (err) => {
expect(err).toBe(error);
done();
});
file.emit('error', error);
expect(writeStreamMock).toBeCalled();
expect(writeStreamInstanceMock.on).toBeCalledWith(
'error',
expect.any(Function),
);
});
test('toJSON()', () => {
const obj = file.toJSON();
expect(obj.mimetype).toBe('image/png');
expect(obj.originalFilename).toBe('cat.png');
});
test('toString()', () => {
expect(file.toString()).toBe('VolatileFile: cat.png');
});
test('write()', (done) => {
const buffer = Buffer.alloc(5);
writeStreamInstanceMock.write.mockImplementationOnce((writeBuffer, cb) => {
expect(buffer).toBe(writeBuffer);
cb();
});
file.write(buffer, () => {
done();
});
});
test('end()', (done) => {
writeStreamInstanceMock.end.mockImplementationOnce((cb) => {
cb();
});
const fileEmitSpy = jest.spyOn(file, 'emit');
file.end(() => done());
expect(fileEmitSpy).toBeCalledWith('end');
});
test('destroy()', () => {
file.destroy();
expect(writeStreamInstanceMock.destroy).toBeCalled();
});
});
================================================
FILE: test-legacy/README.md
================================================
These tests were deleted due to failures when removing the gently lib.
previously in `test/legacy`
================================================
FILE: test-legacy/common.js
================================================
const path = require('path');
const fs = require('fs');
exports.lib = path.join(__dirname, '../../lib');
global.assert = require('assert');
global.TEST_PORT = 13532;
global.TEST_FIXTURES = path.join(__dirname, '../fixture');
global.TEST_TMP = path.join(__dirname, '../tmp');
// Stupid new feature in node that complains about gently attaching too many
// listeners to process 'exit'. This is a workaround until I can think of a
// better way to deal with this.
if (process.setMaxListeners) {
process.setMaxListeners(10000);
}
================================================
FILE: test-legacy/integration/test-multipart-parser.js
================================================
const common = require('../common');
const CHUNK_LENGTH = 10;
const multipartParser = require(`${common.lib}/multipart_parser`);
const { MultipartParser } = multipartParser;
const parser = new MultipartParser();
const fixtures = require(`${TEST_FIXTURES}/multipart`);
Object.keys(fixtures).forEach(function(name) {
const fixture = fixtures[name];
const buffer = Buffer.alloc(Buffer.byteLength(fixture.raw, 'binary'));
let offset = 0;
let chunk;
let nparsed;
const parts = [];
let part = null;
let headerField;
let headerValue;
let endCalled = '';
parser.initWithBoundary(fixture.boundary);
parser.onPartBegin = function() {
part = { headers: {}, data: '' };
parts.push(part);
headerField = '';
headerValue = '';
};
parser.onHeaderField = function(b, start, end) {
headerField += b.toString('ascii', start, end);
};
parser.onHeaderValue = function(b, start, end) {
headerValue += b.toString('ascii', start, end);
};
parser.onHeaderEnd = function() {
part.headers[headerField] = headerValue;
headerField = '';
headerValue = '';
};
parser.onPartData = function(b, start, end) {
const str = b.toString('ascii', start, end);
part.data += b.slice(start, end);
};
parser.onEnd = function() {
endCalled = true;
};
buffer.write(fixture.raw, 0, undefined, 'binary');
while (offset < buffer.length) {
if (offset + CHUNK_LENGTH < buffer.length) {
chunk = buffer.slice(offset, offset + CHUNK_LENGTH);
} else {
chunk = buffer.slice(offset, buffer.length);
}
offset += CHUNK_LENGTH;
nparsed = parser.write(chunk);
if (nparsed != chunk.length) {
if (fixture.expectError) {
return;
}
puts('-- ERROR --');
p(chunk.toString('ascii'));
throw new Error(
`${chunk.length} bytes written, but only ${nparsed} bytes parsed!`,
);
}
}
if (fixture.expectError) {
throw new Error('expected parse error did not happen');
}
assert.ok(endCalled);
assert.deepEqual(parts, fixture.parts);
});
================================================
FILE: test-legacy/simple/test-file.js
================================================
const common = require('../common');
const WriteStreamStub = GENTLY.stub('fs', 'WriteStream');
const File = require(`${common.lib}/file`);
const { EventEmitter } = require('events');
let file;
let gently;
function test(test) {
gently = new Gently();
file = new File();
test();
gently.verify(test.name);
}
test(function constructor() {
assert.ok(file instanceof EventEmitter);
assert.strictEqual(file.size, 0);
assert.strictEqual(file.path, null);
assert.strictEqual(file.name, null);
assert.strictEqual(file.type, null);
assert.strictEqual(file.lastModifiedDate, null);
assert.strictEqual(file._writeStream, null);
(function testSetProperties() {
const file2 = new File({ foo: 'bar' });
assert.equal(file2.foo, 'bar');
})();
});
test(function open() {
let WRITE_STREAM;
file.path = '/foo';
gently.expect(WriteStreamStub, 'new', function(path) {
WRITE_STREAM = this;
assert.strictEqual(path, file.path);
});
file.open();
assert.strictEqual(file._writeStream, WRITE_STREAM);
});
test(function write() {
const BUFFER = { length: 10 };
let CB_STUB;
const CB = function() {
CB_STUB.apply(this, arguments);
};
file._writeStream = {};
gently.expect(file._writeStream, 'write', function(buffer, cb) {
assert.strictEqual(buffer, BUFFER);
gently.expect(file, 'emit', function(event, bytesWritten) {
assert.ok(file.lastModifiedDate instanceof Date);
assert.equal(event, 'progress');
assert.equal(bytesWritten, file.size);
});
CB_STUB = gently.expect(function writeCb() {
assert.equal(file.size, 10);
});
cb();
gently.expect(file, 'emit', function(event, bytesWritten) {
assert.equal(event, 'progress');
assert.equal(bytesWritten, file.size);
});
CB_STUB = gently.expect(function writeCb() {
assert.equal(file.size, 20);
});
cb();
});
file.write(BUFFER, CB);
});
test(function end() {
let CB_STUB;
const CB = function() {
CB_STUB.apply(this, arguments);
};
file._writeStream = {};
gently.expect(file._writeStream, 'end', function(cb) {
gently.expect(file, 'emit', function(event) {
assert.equal(event, 'end');
});
CB_STUB = gently.expect(function endCb() {});
cb();
});
file.end(CB);
});
================================================
FILE: test-legacy/simple/test-incoming-form.js
================================================
const common = require('../common');
const MultipartParserStub = GENTLY.stub(
'./multipart_parser',
'MultipartParser',
);
const QuerystringParserStub = GENTLY.stub(
'./querystring_parser',
'QuerystringParser',
);
const EventEmitterStub = GENTLY.stub('events', 'EventEmitter');
const StreamStub = GENTLY.stub('stream', 'Stream');
const FileStub = GENTLY.stub('./file');
const formidable = require(`${common.lib}/index`);
const { IncomingForm } = formidable;
const events = require('events');
const fs = require('fs');
const path = require('path');
const fixtures = require(`${TEST_FIXTURES}/multipart`);
let form;
let gently;
function test(test) {
gently = new Gently();
gently.expect(EventEmitterStub, 'call');
form = new IncomingForm();
test();
gently.verify(test.name);
}
test(function constructor() {
assert.strictEqual(form.error, null);
assert.strictEqual(form.ended, false);
assert.strictEqual(form.type, null);
assert.strictEqual(form.headers, null);
assert.strictEqual(form.keepExtensions, false);
// Can't assume dir === '/tmp' for portability
// assert.strictEqual(form.uploadDir, '/tmp');
// Make sure it is a directory instead
assert.doesNotThrow(function() {
assert(fs.statSync(form.uploadDir).isDirectory());
});
assert.strictEqual(form.encoding, 'utf-8');
assert.strictEqual(form.bytesReceived, null);
assert.strictEqual(form.bytesExpected, null);
assert.strictEqual(form.maxFieldsSize, 20 * 1024 * 1024);
assert.strictEqual(form._parser, null);
assert.strictEqual(form._flushing, 0);
assert.strictEqual(form._fieldsSize, 0);
assert.ok(form instanceof EventEmitterStub);
assert.equal(form.constructor.name, 'IncomingForm');
(function testSimpleConstructor() {
gently.expect(EventEmitterStub, 'call');
const form = IncomingForm();
assert.ok(form instanceof IncomingForm);
})();
(function testSimpleConstructorShortcut() {
gently.expect(EventEmitterStub, 'call');
const form = formidable();
assert.ok(form instanceof IncomingForm);
})();
});
test(function parse() {
const REQ = { headers: {} };
const emit = {};
gently.expect(form, 'writeHeaders', function(headers) {
assert.strictEqual(headers, REQ.headers);
});
const EVENTS = ['error', 'aborted', 'data', 'end'];
gently.expect(REQ, 'on', EVENTS.length, function(event, fn) {
assert.equal(event, EVENTS.shift());
emit[event] = fn;
return this;
});
form.parse(REQ);
(function testPause() {
gently.expect(REQ, 'pause');
assert.strictEqual(form.pause(), true);
})();
(function testPauseCriticalException() {
form.ended = false;
const ERR = new Error('dasdsa');
gently.expect(REQ, 'pause', function() {
throw ERR;
});
gently.expect(form, '_error', function(err) {
assert.strictEqual(err, ERR);
});
assert.strictEqual(form.pause(), false);
})();
(function testPauseHarmlessException() {
form.ended = true;
const ERR = new Error('dasdsa');
gently.expect(REQ, 'pause', function() {
throw ERR;
});
assert.strictEqual(form.pause(), false);
})();
(function testResume() {
gently.expect(REQ, 'resume');
assert.strictEqual(form.resume(), true);
})();
(function testResumeCriticalException() {
form.ended = false;
const ERR = new Error('dasdsa');
gently.expect(REQ, 'resume', function() {
throw ERR;
});
gently.expect(form, '_error', function(err) {
assert.strictEqual(err, ERR);
});
assert.strictEqual(form.resume(), false);
})();
(function testResumeHarmlessException() {
form.ended = true;
const ERR = new Error('dasdsa');
gently.expect(REQ, 'resume', function() {
throw ERR;
});
assert.strictEqual(form.resume(), false);
})();
(function testEmitError() {
const ERR = new Error('something bad happened');
gently.expect(form, '_error', function(err) {
assert.strictEqual(err, ERR);
});
emit.error(ERR);
})();
(function testEmitAborted() {
gently.expect(form, 'emit', function(event) {
assert.equal(event, 'aborted');
});
gently.expect(form, '_error');
emit.aborted();
})();
(function testEmitData() {
const BUFFER = [1, 2, 3];
gently.expect(form, 'write', function(buffer) {
assert.strictEqual(buffer, BUFFER);
});
emit.data(BUFFER);
})();
(function testEmitEnd() {
form._parser = {};
(function testWithError() {
const ERR = new Error('haha');
gently.expect(form._parser, 'end', function() {
return ERR;
});
gently.expect(form, '_error', function(err) {
assert.strictEqual(err, ERR);
});
emit.end();
})();
(function testWithoutError() {
gently.expect(form._parser, 'end');
emit.end();
})();
(function testAfterError() {
form.error = true;
emit.end();
})();
})();
(function testWithCallback() {
gently.expect(EventEmitterStub, 'call');
const form = new IncomingForm();
const REQ = { headers: {} };
const parseCalled = 0;
gently.expect(form, 'on', 4, function(event, fn) {
if (event == 'field') {
fn('field1', 'foo');
fn('field1', 'bar');
fn('field2', 'nice');
}
if (event == 'file') {
fn('file1', '1');
fn('file1', '2');
fn('file2', '3');
}
if (event == 'end') {
fn();
}
return this;
});
gently.expect(form, 'writeHeaders');
gently.expect(REQ, 'on', 4, function() {
return this;
});
const parseCbOk = function(err, fields, files) {
// assert.deepEqual(fields, {field1: ['foo', 'bar'], field2: 'nice'});
assert.deepEqual(files, { file1: '2', file2: '3' });
};
form.parse(REQ, parseCbOk);
const ERR = new Error('test');
gently.expect(form, 'on', 3, function(event, fn) {
if (event == 'field') {
fn('foo', 'bar');
}
if (event == 'error') {
fn(ERR);
gently.expect(form, 'on');
gently.expect(form, 'writeHeaders');
gently.expect(REQ, 'on', 4, function() {
return this;
});
}
return this;
});
form.parse(REQ, function parseCbErr(err, fields, files) {
assert.strictEqual(err, ERR);
assert.deepEqual(fields, { foo: 'bar' });
});
})();
(function testWriteOrder() {
gently.expect(EventEmitterStub, 'call');
const form = new IncomingForm();
const REQ = new events.EventEmitter();
const BUF = {};
const DATACB = null;
REQ.on('newListener', function(event, fn) {
if (event === 'data') fn(BUF);
});
gently.expect(form, 'writeHeaders');
gently.expect(form, 'write', function(buf) {
assert.strictEqual(buf, BUF);
});
form.parse(REQ);
})();
});
test(function pause() {
assert.strictEqual(form.pause(), false);
});
test(function resume() {
assert.strictEqual(form.resume(), false);
});
test(function writeHeaders() {
const HEADERS = {};
gently.expect(form, '_parseContentLength');
gently.expect(form, '_parseContentType');
form.writeHeaders(HEADERS);
assert.strictEqual(form.headers, HEADERS);
});
test(function write() {
const parser = {};
const BUFFER = [1, 2, 3];
form._parser = parser;
form.bytesExpected = 523423;
(function testBasic() {
gently.expect(form, 'emit', function(event, bytesReceived, bytesExpected) {
assert.equal(event, 'progress');
assert.equal(bytesReceived, BUFFER.length);
assert.equal(bytesExpected, form.bytesExpected);
});
gently.expect(parser, 'write', function(buffer) {
assert.strictEqual(buffer, BUFFER);
return buffer.length;
});
assert.equal(form.write(BUFFER), BUFFER.length);
assert.equal(form.bytesReceived, BUFFER.length);
})();
(function testParserError() {
gently.expect(form, 'emit');
gently.expect(parser, 'write', function(buffer) {
assert.strictEqual(buffer, BUFFER);
return buffer.length - 1;
});
gently.expect(form, '_error', function(err) {
assert.ok(err.message.match(/parser error/i));
});
assert.equal(form.write(BUFFER), BUFFER.length - 1);
assert.equal(form.bytesReceived, BUFFER.length + BUFFER.length);
})();
(function testUninitialized() {
delete form._parser;
gently.expect(form, '_error', function(err) {
assert.ok(err.message.match(/uninitialized parser/i));
});
form.write(BUFFER);
})();
});
test(function parseContentType() {
const HEADERS = {};
form.headers = { 'content-type': 'application/x-www-form-urlencoded' };
gently.expect(form, '_initUrlencoded');
form._parseContentType();
// accept anything that has 'urlencoded' in it
form.headers = { 'content-type': 'broken-client/urlencoded-stupid' };
gently.expect(form, '_initUrlencoded');
form._parseContentType();
const BOUNDARY = '---------------------------57814261102167618332366269';
form.headers = {
'content-type': `multipart/form-data; boundary=${BOUNDARY}`,
};
gently.expect(form, '_initMultipart', function(boundary) {
assert.equal(boundary, BOUNDARY);
});
form._parseContentType();
(function testQuotedBoundary() {
form.headers = {
'content-type': `multipart/form-data; boundary="${BOUNDARY}"`,
};
gently.expect(form, '_initMultipart', function(boundary) {
assert.equal(boundary, BOUNDARY);
});
form._parseContentType();
})();
(function testNoBoundary() {
form.headers = { 'content-type': 'multipart/form-data' };
gently.expect(form, '_error', function(err) {
assert.ok(err.message.match(/no multipart boundary/i));
});
form._parseContentType();
})();
(function testNoContentType() {
form.headers = {};
gently.expect(form, '_error', function(err) {
assert.ok(err.message.match(/no content-type/i));
});
form._parseContentType();
})();
(function testUnknownContentType() {
form.headers = { 'content-type': 'invalid' };
gently.expect(form, '_error', function(err) {
assert.ok(err.message.match(/unknown content-type/i));
});
form._parseContentType();
})();
});
test(function parseContentLength() {
const HEADERS = {};
form.headers = {};
gently.expect(form, 'emit', function(event, bytesReceived, bytesExpected) {
assert.equal(event, 'progress');
assert.equal(bytesReceived, 0);
assert.equal(bytesExpected, 0);
});
form._parseContentLength();
form.headers['content-length'] = '8';
gently.expect(form, 'emit', function(event, bytesReceived, bytesExpected) {
assert.equal(event, 'progress');
assert.equal(bytesReceived, 0);
assert.equal(bytesExpected, 8);
});
form._parseContentLength();
assert.strictEqual(form.bytesReceived, 0);
assert.strictEqual(form.bytesExpected, 8);
// JS can be evil, lets make sure we are not
form.headers['content-length'] = '08';
gently.expect(form, 'emit', function(event, bytesReceived, bytesExpected) {
assert.equal(event, 'progress');
assert.equal(bytesReceived, 0);
assert.equal(bytesExpected, 8);
});
form._parseContentLength();
assert.strictEqual(form.bytesExpected, 8);
});
test(function _initMultipart() {
const BOUNDARY = '123';
let PARSER;
gently.expect(MultipartParserStub, 'new', function() {
PARSER = this;
});
gently.expect(MultipartParserStub.prototype, 'initWithBoundary', function(
boundary,
) {
assert.equal(boundary, BOUNDARY);
});
form._initMultipart(BOUNDARY);
assert.equal(form.type, 'multipart');
assert.strictEqual(form._parser, PARSER);
(function testRegularField() {
let PART;
gently.expect(StreamStub, 'new', function() {
PART = this;
});
gently.expect(form, 'onPart', function(part) {
assert.strictEqual(part, PART);
assert.deepEqual(part.headers, {
'content-disposition': 'form-data; name="field1"',
foo: 'bar',
});
assert.equal(part.name, 'field1');
const strings = ['hello', ' world'];
gently.expect(part, 'emit', 2, function(event, b) {
assert.equal(event, 'data');
assert.equal(b.toString(), strings.shift());
});
gently.expect(part, 'emit', function(event, b) {
assert.equal(event, 'end');
});
});
PARSER.onPartBegin();
PARSER.onHeaderField(Buffer.from('content-disposition'), 0, 10);
PARSER.onHeaderField(Buffer.from('content-disposition'), 10, 19);
PARSER.onHeaderValue(Buffer.from('form-data; name="field1"'), 0, 14);
PARSER.onHeaderValue(Buffer.from('form-data; name="field1"'), 14, 24);
PARSER.onHeaderEnd();
PARSER.onHeaderField(Buffer.from('foo'), 0, 3);
PARSER.onHeaderValue(Buffer.from('bar'), 0, 3);
PARSER.onHeaderEnd();
PARSER.onHeadersEnd();
PARSER.onPartData(Buffer.from('hello world'), 0, 5);
PARSER.onPartData(Buffer.from('hello world'), 5, 11);
PARSER.onPartEnd();
})();
(function testFileField() {
let PART;
gently.expect(StreamStub, 'new', function() {
PART = this;
});
gently.expect(form, 'onPart', function(part) {
assert.deepEqual(part.headers, {
'content-disposition':
'form-data; name="field2"; filename="C:\\Documents and Settings\\IE\\Must\\Die\\Sun"et.jpg"',
'content-type': 'text/plain',
});
assert.equal(part.name, 'field2');
assert.equal(part.filename, 'Sun"et.jpg');
assert.equal(part.mime, 'text/plain');
gently.expect(part, 'emit', function(event, b) {
assert.equal(event, 'data');
assert.equal(b.toString(), '... contents of file1.txt ...');
});
gently.expect(part, 'emit', function(event, b) {
assert.equal(event, 'end');
});
});
PARSER.onPartBegin();
PARSER.onHeaderField(Buffer.from('content-disposition'), 0, 19);
PARSER.onHeaderValue(
Buffer.from(
'form-data; name="field2"; filename="C:\\Documents and Settings\\IE\\Must\\Die\\Sun"et.jpg"',
),
0,
85,
);
PARSER.onHeaderEnd();
PARSER.onHeaderField(Buffer.from('Content-Type'), 0, 12);
PARSER.onHeaderValue(Buffer.from('text/plain'), 0, 10);
PARSER.onHeaderEnd();
PARSER.onHeadersEnd();
PARSER.onPartData(Buffer.from('... contents of file1.txt ...'), 0, 29);
PARSER.onPartEnd();
})();
(function testEnd() {
gently.expect(form, '_maybeEnd');
PARSER.onEnd();
assert.ok(form.ended);
})();
});
test(function _fileName() {
// TODO
});
test(function _initUrlencoded() {
let PARSER;
gently.expect(QuerystringParserStub, 'new', function() {
PARSER = this;
});
form._initUrlencoded();
assert.equal(form.type, 'urlencoded');
assert.strictEqual(form._parser, PARSER);
(function testOnField() {
const KEY = 'KEY';
const VAL = 'VAL';
gently.expect(form, 'emit', function(field, key, val) {
assert.equal(field, 'field');
assert.equal(key, KEY);
assert.equal(val, VAL);
});
PARSER.onField(KEY, VAL);
})();
(function testOnEnd() {
gently.expect(form, '_maybeEnd');
PARSER.onEnd();
assert.equal(form.ended, true);
})();
});
test(function _error() {
const ERR = new Error('bla');
gently.expect(form, 'emit', function(event, err) {
assert.equal(event, 'error');
assert.strictEqual(err, ERR);
});
form._error(ERR);
assert.strictEqual(form.error, ERR);
// make sure _error only does its thing once
form._error(ERR);
});
test(function onPart() {
const PART = {};
gently.expect(form, 'handlePart', function(part) {
assert.strictEqual(part, PART);
});
form.onPart(PART);
});
test(function handlePart() {
(function testUtf8Field() {
const PART = new events.EventEmitter();
PART.name = 'my_field';
gently.expect(form, 'emit', function(event, field, value) {
assert.equal(event, 'field');
assert.equal(field, 'my_field');
assert.equal(value, 'hello world: €');
});
form.handlePart(PART);
PART.emit('data', Buffer.from('hello'));
PART.emit('data', Buffer.from(' world: '));
PART.emit('data', Buffer.from([0xe2]));
PART.emit('data', Buffer.from([0x82, 0xac]));
PART.emit('end');
})();
(function testBinaryField() {
const PART = new events.EventEmitter();
PART.name = 'my_field2';
gently.expect(form, 'emit', function(event, field, value) {
assert.equal(event, 'field');
assert.equal(field, 'my_field2');
assert.equal(
value,
`hello world: ${Buffer.from([0xe2, 0x82, 0xac]).toString('binary')}`,
);
});
form.encoding = 'binary';
form.handlePart(PART);
PART.emit('data', Buffer.from('hello'));
PART.emit('data', Buffer.from(' world: '));
PART.emit('data', Buffer.from([0xe2]));
PART.emit('data', Buffer.from([0x82, 0xac]));
PART.emit('end');
})();
(function testFieldSize() {
form.maxFieldsSize = 8;
const PART = new events.EventEmitter();
PART.name = 'my_field';
gently.expect(form, '_error', function(err) {
assert.equal(
err.message,
'maxFieldsSize exceeded, received 9 bytes of field data',
);
});
form.handlePart(PART);
form._fieldsSize = 1;
PART.emit('data', Buffer.alloc(7));
PART.emit('data', Buffer.alloc(1));
})();
(function testFilePart() {
const PART = new events.EventEmitter();
let FILE = new events.EventEmitter();
const PATH = '/foo/bar';
PART.name = 'my_file';
PART.filename = 'sweet.txt';
PART.mime = 'sweet.txt';
gently.expect(form, '_uploadPath', function(filename) {
assert.equal(filename, PART.filename);
return PATH;
});
gently.expect(FileStub, 'new', function(properties) {
assert.equal(properties.path, PATH);
assert.equal(properties.name, PART.filename);
assert.equal(properties.type, PART.mime);
FILE = this;
gently.expect(form, 'emit', function(event, field, file) {
assert.equal(event, 'fileBegin');
assert.strictEqual(field, PART.name);
assert.strictEqual(file, FILE);
});
gently.expect(FILE, 'open');
});
form.handlePart(PART);
assert.equal(form._flushing, 1);
let BUFFER;
gently.expect(form, 'pause');
gently.expect(FILE, 'write', function(buffer, cb) {
assert.strictEqual(buffer, BUFFER);
gently.expect(form, 'resume');
// @todo handle cb(new Err)
cb();
});
PART.emit('data', (BUFFER = Buffer.from('test')));
gently.expect(FILE, 'end', function(cb) {
gently.expect(form, 'emit', function(event, field, file) {
assert.equal(event, 'file');
assert.strictEqual(file, FILE);
});
gently.expect(form, '_maybeEnd');
cb();
assert.equal(form._flushing, 0);
});
PART.emit('end');
})();
});
test(function _uploadPath() {
(function testUniqueId() {
let UUID_A;
let UUID_B;
gently.expect(GENTLY.hijacked.path, 'join', function(uploadDir, uuid) {
assert.equal(uploadDir, form.uploadDir);
UUID_A = uuid;
});
form._uploadPath();
gently.expect(GENTLY.hijacked.path, 'join', function(uploadDir, uuid) {
UUID_B = uuid;
});
form._uploadPath();
assert.notEqual(UUID_A, UUID_B);
})();
(function testFileExtension() {
form.keepExtensions = true;
const FILENAME = 'foo.jpg';
const EXT = '.bar';
gently.expect(GENTLY.hijacked.path, 'extname', function(filename) {
assert.equal(filename, FILENAME);
gently.restore(path, 'extname');
return EXT;
});
gently.expect(GENTLY.hijacked.path, 'join', function(uploadDir, name) {
assert.equal(path.extname(name), EXT);
});
form._uploadPath(FILENAME);
})();
});
test(function _maybeEnd() {
gently.expect(form, 'emit', 0);
form._maybeEnd();
form.ended = true;
form._flushing = 1;
form._maybeEnd();
gently.expect(form, 'emit', function(event) {
assert.equal(event, 'end');
});
form.ended = true;
form._flushing = 0;
form._maybeEnd();
});
================================================
FILE: test-legacy/system/test-multi-video-upload.js
================================================
const common = require('../common');
const BOUNDARY = '---------------------------10102754414578508781458777923';
const FIXTURE = `${TEST_FIXTURES}/multi_video.upload`;
const fs = require('fs');
const http = require('http');
const formidable = require(`${common.lib}/index`);
const server = http.createServer();
server.on('request', function(req, res) {
const form = new formidable.IncomingForm();
const uploads = {};
form.uploadDir = TEST_TMP;
form.hash = 'sha1';
form.parse(req);
form
.on('fileBegin', function(field, file) {
assert.equal(field, 'upload');
const tracker = { file, progress: [], ended: false };
uploads[file.name] = tracker;
file
.on('progress', function(bytesReceived) {
tracker.progress.push(bytesReceived);
assert.equal(bytesReceived, file.size);
})
.on('end', function() {
tracker.ended = true;
});
})
.on('field', function(field, value) {
assert.equal(field, 'title');
assert.equal(value, '');
})
.on('file', function(field, file) {
assert.equal(field, 'upload');
assert.strictEqual(uploads[file.name].file, file);
})
.on('end', function() {
assert.ok(uploads['shortest_video.flv']);
assert.ok(uploads['shortest_video.flv'].ended);
assert.ok(uploads['shortest_video.flv'].progress.length > 3);
assert.equal(
uploads['shortest_video.flv'].file.hash,
'd6a17616c7143d1b1438ceeef6836d1a09186b3a',
);
assert.equal(
uploads['shortest_video.flv'].progress.slice(-1),
uploads['shortest_video.flv'].file.size,
);
assert.ok(uploads['shortest_video.mp4']);
assert.ok(uploads['shortest_video.mp4'].ended);
assert.ok(uploads['shortest_video.mp4'].progress.length > 3);
assert.equal(
uploads['shortest_video.mp4'].file.hash,
'937dfd4db263f4887ceae19341dcc8d63bcd557f',
);
server.close();
res.writeHead(200);
res.end('good');
});
});
server.listen(TEST_PORT, function() {
let stat;
let headers;
let request;
let fixture;
stat = fs.statSync(FIXTURE);
request = http.request({
port: TEST_PORT,
path: '/',
method: 'POST',
headers: {
'content-type': `multipart/form-data; boundary=${BOUNDARY}`,
'content-length': stat.size,
},
});
fs.createReadStream(FIXTURE).pipe(request);
});
================================================
FILE: test-node/standalone/createDirsFromUploads.test.js
================================================
import {strictEqual, deepEqual} from 'node:assert';
import { createServer, request } from 'node:http';
import formidable from '../../src/index.js';
import test from 'node:test';
import fs from 'node:fs';
const PORT = 13539;
const uploads = './uploads';
test('folder created', (t,done) => {
const server = createServer((req, res) => {
const form = formidable({
createDirsFromUploads: true,
uploadDir: uploads,
filename: (x) => {
return 'x/y/z.txt'
}
});
form.parse(req, () => {
res.writeHead(200);
res.end("ok")
});
});
server.listen(PORT, () => {
const chosenPort = server.address().port;
const body = `----13068458571765726332503797717\r
Content-Disposition: form-data; name="title"\r
\r
a\r
----13068458571765726332503797717\r
Content-Disposition: form-data; name="multipleFiles"; filename="x.txt"\r
Content-Type: application/x-javascript\r
\r
\r
\r
a\r
b\r
c\r
d\r
\r
----13068458571765726332503797717--\r
`;
fetch(String(new URL(`http:localhost:${chosenPort}/`)), {
method: 'POST',
headers: {
'Content-Length': body.length,
Host: `localhost:${chosenPort}`,
'Content-Type': 'multipart/form-data; boundary=--13068458571765726332503797717',
},
body
}).then(res => {
//may also contain tests from other tests
deepEqual(fs.readdirSync(uploads).includes('x'), true);
deepEqual(fs.readdirSync(`${uploads}/x`), ['y']);
deepEqual(fs.readdirSync(`${uploads}/x/y`), ['z.txt']);
strictEqual(res.status, 200);
server.close();
done();
});
});
});
================================================
FILE: test-node/standalone/end-event-emitted-twice.test.js
================================================
import {strictEqual} from 'node:assert';
import { createServer, request } from 'node:http';
import formidable from '../../src/index.js';
import test from 'node:test';
const PORT = 13540;
test('end event emitted twice', (t,done) => {
const server = createServer((req, res) => {
const form = formidable();
let i = 0;
form.on('end', () => {
i += 1;
strictEqual(i, 1, 'end should be emitted once (on end)');
});
form.parse(req, () => {
try {
strictEqual(i, 1, 'end should be emitted once (callback)');
} catch (e) {
done(e);
}
res.writeHead(200);
res.end("ok")
});
});
server.listen(PORT, () => {
const chosenPort = server.address().port;
const body = `----13068458571765726332503797717\r
Content-Disposition: form-data; name="title"\r
\r
a\r
----13068458571765726332503797717\r
Content-Disposition: form-data; name="multipleFiles"; filename="x.txt"\r
Content-Type: application/x-javascript\r
\r
\r
\r
a\r
b\r
c\r
d\r
\r
----13068458571765726332503797717--\r
`;
fetch(String(new URL(`http:localhost:${chosenPort}/`)), {
method: 'POST',
headers: {
'Content-Length': body.length,
Host: `localhost:${chosenPort}`,
'Content-Type': 'multipart/form-data; boundary=--13068458571765726332503797717',
},
body
}).then(res => {
strictEqual(res.status, 200);
server.close();
done();
});
});
});
================================================
FILE: test-node/standalone/multipart_parser.test.js
================================================
import {Readable} from 'node:stream';
import MultipartParser from '../../src/parsers/Multipart.js';
import {malformedMultipart} from '../../src/FormidableError.js';
import test from 'node:test';
import assert, { deepEqual } from 'node:assert';
test('MultipartParser does not hang', async (t) => {
const mime = `--_\r\n--_--\r\n`;
const parser = new MultipartParser();
parser.initWithBoundary('_');
try {
for await (const {name, buffer, start, end} of Readable.from(mime).pipe(parser)) {
console.log(name, buffer ? buffer.subarray(start, end).toString() : '');
}
} catch (e) {
deepEqual(e.code, malformedMultipart)
return;
// console.error('error');
// console.error(e);
}
assert(false, 'should catch error');
});
================================================
FILE: test-node/standalone/promise.test.js
================================================
import { ok, strictEqual } from 'node:assert';
import { createServer } from 'node:http';
import test from 'node:test';
import formidable, { errors } from '../../src/index.js';
const isPromise = (x) => {
return x && typeof x === `object` && typeof x.then === `function`;
};
let server;
let port = 13540;
test.beforeEach(() => {
// Increment port to avoid conflicts between tests
port += 1;
server = createServer();
});
test.afterEach(() => {
return new Promise((resolve) => {
if (server.listening) {
server.close(() => resolve());
} else {
resolve();
}
});
});
test('parse returns promise if no callback is provided', async (t) => {
server.on('request', (req, res) => {
const form = formidable();
const promise = form.parse(req);
strictEqual(isPromise(promise), true);
promise.then(([fields, files]) => {
ok(typeof fields === 'object');
ok(typeof files === 'object');
res.writeHead(200);
res.end("ok");
}).catch(e => {
res.writeHead(500);
res.end(String(e));
});
});
await new Promise(resolve => server.listen(port, resolve));
const body = `----13068458571765726332503797717\r
Content-Disposition: form-data; name="title"\r
\r
a\r
----13068458571765726332503797717\r
Content-Disposition: form-data; name="multipleFiles"; filename="x.txt"\r
Content-Type: application/x-javascript\r
\r
\r
\r
a\r
b\r
c\r
d\r
\r
----13068458571765726332503797717--\r
`;
const res = await fetch(String(new URL(`http:localhost:${port}/`)), {
method: 'POST',
headers: {
'Content-Length': body.length,
Host: `localhost:${port}`,
'Content-Type': 'multipart/form-data; boundary=--13068458571765726332503797717',
},
body
});
strictEqual(res.status, 200);
});
test('parse rejects with promise if it fails', async (t) => {
server.on('request', (req, res) => {
const form = formidable({minFileSize: 10 ** 6}); // create condition to fail
const promise = form.parse(req);
strictEqual(isPromise(promise), true);
promise.then(() => {
res.writeHead(500);
res.end('should have failed');
}).catch(e => {
res.writeHead(e.httpCode);
strictEqual(e.code, errors.smallerThanMinFileSize);
res.end(String(e));
});
});
await new Promise(resolve => server.listen(port, resolve));
const body = `----13068458571765726332503797717\r
Content-Disposition: form-data; name="title"\r
\r
a\r
----13068458571765726332503797717\r
Content-Disposition: form-data; name="multipleFiles"; filename="x.txt"\r
Content-Type: application/x-javascript\r
\r
\r
\r
a\r
b\r
c\r
d\r
\r
----13068458571765726332503797717--\r
`;
const res = await fetch(String(new URL(`http:localhost:${port}/`)), {
method: 'POST',
headers: {
'Content-Length': body.length,
Host: `localhost:${port}`,
'Content-Type': 'multipart/form-data; boundary=--13068458571765726332503797717',
},
body
});
strictEqual(res.status, 400);
});
================================================
FILE: tool/record.js
================================================
/* eslint-disable no-param-reassign */
const http = require('http');
const fs = require('fs');
let connections = 0;
const server = http.createServer((req, res) => {
const { socket } = req;
console.log('Request: %s %s -> %s', req.method, req.url, socket.filename);
req.on('end', () => {
if (req.url !== '/') {
res.end(
JSON.stringify({
method: req.method,
url: req.url,
filename: socket.filename,
}),
);
return;
}
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(
'' +
' ' +
' ' +
' ' +
' ',
);
});
});
server.on('connection', (socket) => {
connections += 1;
socket.id = connections;
socket.filename = `connection-${socket.id}.http`;
socket.file = fs.createWriteStream(socket.filename);
socket.pipe(socket.file);
console.log('--> %s', socket.filename);
socket.on('close', () => {
console.log('<-- %s', socket.filename);
});
});
const port = process.env.PORT || 8080;
server.listen(port, () => {
console.log('Recording connections on port %s', port);
});
================================================
FILE: tool/rollup.config.js
================================================
/* eslint-disable */
import cjs from '@rollup/plugin-commonjs';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import packageJson from '../package.json' with { type: "json" };
const {dependencies} = packageJson;
const plugins = [nodeResolve(), cjs()];
const cjsOptions = {
format: `cjs`,
exports: `named`,
}
const external = [...Object.keys(dependencies)];
export default [
{
input: `src/index.js`,
output: [
{
file: `dist/index.cjs`,
...cjsOptions,
},
],
external,
plugins,
},
{
input: `src/helpers/firstValues.js`,
output: [
{
file: `dist/helpers/firstValues.cjs`,
...cjsOptions,
},
],
external,
plugins,
},
{
input: `src/helpers/readBooleans.js`,
output: [
{
file: `dist/helpers/readBooleans.cjs`,
...cjsOptions,
},
],
external,
plugins,
},
{
input: `src/parsers/JSON.js`,
output: [
{
file: `dist/parsers/JSON.cjs`,
...cjsOptions,
},
],
external,
plugins,
},
{
input: `src/parsers/Multipart.js`,
output: [
{
file: `dist/parsers/Multipart.cjs`,
...cjsOptions,
},
],
external,
plugins,
},
{
input: `src/parsers/Querystring.js`,
output: [
{
file: `dist/parsers/Querystring.cjs`,
...cjsOptions,
},
],
external,
plugins,
},
{
input: `src/parsers/OctetStream.js`,
output: [
{
file: `dist/parsers/OctetStream.cjs`,
...cjsOptions,
},
],
external,
plugins,
},
{
input: `src/parsers/StreamingQuerystring.js`,
output: [
{
file: `dist/parsers/StreamingQuerystring.cjs`,
...cjsOptions,
},
],
external,
plugins,
},
];