From 05cc2232d894ba517d1e5cf2f344341509f0579e Mon Sep 17 00:00:00 2001 From: Vedant K Date: Sat, 26 Aug 2023 21:10:20 +0530 Subject: [PATCH 01/38] docs: update license and readme --- LICENSE | 21 ------------- README.md | 59 ------------------------------------- license.md | 20 +++++++++++++ readme.md | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 80 deletions(-) delete mode 100644 LICENSE delete mode 100644 README.md create mode 100644 license.md create mode 100644 readme.md diff --git a/LICENSE b/LICENSE deleted file mode 100644 index bfa23e5..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2023 Express Rate Limit - -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. diff --git a/README.md b/README.md deleted file mode 100644 index e77520a..0000000 --- a/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# ratelimit-header-parser - -[![CI](https://github.com/express-rate-limit/ratelimit-header-parser/actions/workflows/ci.yml/badge.svg)](https://github.com/express-rate-limit/ratelimit-header-parser/actions/workflows/ci.yml) -[![npm version](https://img.shields.io/npm/v/ratelimit-header-parser.svg)](https://npmjs.org/package/ratelimit-header-parser 'View this project on NPM') - -Parse RateLimit headers of various forms into a normalized format. Supports the -combined form from -[draft 7](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers-07) -of the -[IETF Rate Limit Headers standard](https://github.com/ietf-wg-httpapi/ratelimit-headers), -the uncombined `RateLimit-*` format of earlier drafts, traditional -`X-RateLimit-*` headers, and a few other formats. - -## Usage: - -```js -import { parseRateLimit } from 'ratelimit-header-parser' - -const response = await fetch( - 'https://api.github.com/repos/express-rate-limit/express-rate-limit/contributors?anon=1', -) - -console.log('github ratelimit:', parseRateLimit(response)) -// > github ratelimit: { limit: 60, used: 1, remaining: 59, reset: 2023-08-25T04:16:48.000Z } -``` - -## API - -### parseRateLimit(responseOrHeadersObject, [options]) => Object | undefined - -Scans the input for ratelimit headers in a variety of formats and returns the -result in a consistent format, or undefined if it fails to find any ratelimit -headers. - -- `responseOrHeadersObject`: May be either a fetch-style Response or Headers - object or a node.js-style response or headers object -- `options`: Optional object with the following optional fields: - - `reset`: How to parse the reset field. If unset, the parser will guess based - on the content. Accepts the following strings: - - `'date'`: past the value to `new Date(...)` to let the JavaScript engine - parse it - - `'unix'`: treat the value as the number of seconds since January 1, 1970 - (A.K.A a unix timestamp) - - `'seconds'`: treat the value as the number of seconds from the current - time - - `'milliseconds'`: treat the value as the number of milliseconds from the - current time - -Returns a object with the following fields, or undefined if it does not find any -rate-limit headers. - -``` -{ - limit: number - used: number - remaining: number - reset: Date or undefined -} -``` diff --git a/license.md b/license.md new file mode 100644 index 0000000..3111ef9 --- /dev/null +++ b/license.md @@ -0,0 +1,20 @@ +# MIT License + +Copyright (c) 2023 Express Rate Limit + +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. diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..01c6a15 --- /dev/null +++ b/readme.md @@ -0,0 +1,86 @@ +#
`RateLimit` Header Parser
+ +
+ +[![tests](https://github.com/express-rate-limit/ratelimit-header-parser/actions/workflows/ci.yaml/badge.svg)](https://github.com/express-rate-limit/ratelimit-header-parser/actions/workflows/ci.yaml) +[![npm version](https://img.shields.io/npm/v/ratelimit-header-parser.svg)](https://npmjs.org/package/ratelimit-header-parser 'View this project on NPM') +[![npm downloads](https://img.shields.io/npm/dm/ratelimit-header-parser)](https://www.npmjs.com/package/ratelimit-header-parser) + +This library parses `RateLimit` headers of various forms into a normalized +format. It supports the combined format specified in +[draft 7](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers-07) +of the +[IETF Rate Limit Headers standard](https://github.com/ietf-wg-httpapi/ratelimit-headers), +the uncombined `RateLimit-*` format of earlier drafts, traditional +`X-RateLimit-*` headers, and a few other formats. + +
+ +## Usage + +```ts +import { parseRateLimit } from 'ratelimit-header-parser' + +const response = await fetch( + 'https://api.github.com/repos/express-rate-limit/express-rate-limit/contributors?anon=1', +) + +console.log('github ratelimit:', parseRateLimit(response)) +// > github ratelimit: { limit: 60, used: 1, remaining: 59, reset: 2023-08-25T04:16:48.000Z } +``` + +For more examples, take a look at the [`examples/`](examples/) folder. + +## API + +### `parseRateLimit(responseOrHeaders, [options]) => object | undefined` + +Scans the input for ratelimit headers in a variety of formats and returns the +result in a consistent format, or undefined if it fails to find any rate-limit +headers. Returns an object with the following fields, or `undefined` if it does +not find any rate-limit headers. + +```ts +type RateLimitInfo = { + limit: number + used: number + remaining: number + reset: Date | undefined +} +``` + +#### `responseOrHeaders` + +> A node-style or fetch-style `Response`/`Headers` object. + +#### `options` + +> Options that configure how the library parses the headers. + +```ts +type Options = { + // How to parse the `reset` field. If unset, the parser will guess based on + // the content of the header. + reset: | + 'date' | // Pass the value to `new Date(...)` to let the JavaScript engine parse it. + 'unix' | // Treat the value as the number of seconds since January 1, 1970 (A.K.A a UNIX epoch timestamp). + 'seconds' | // Treat the value as the number of seconds from the current time. + 'milliseconds' | // Treat the value as the number of milliseconds from the current time. +} +``` + +## Issues and Contributing + +If you encounter a bug or want to see something added/changed, please go ahead +and +[open an issue](https://github.com/nfriexpress-rate-limitedly/ratelimit-header-parser/issues/new)! +If you need help with something, feel free to +[start a discussion](https://github.com/express-rate-limit/ratelimit-header-parser/discussions/new)! + +If you wish to contribute to the library, thanks! First, please read +[the contributing guide](contributing.md). Then you can pick up any issue and +fix/implement it! + +## License + +MIT © Express Rate Limit From 1569e08f57095790ef9bfc1a032dc3a43577cd0e Mon Sep 17 00:00:00 2001 From: Vedant K Date: Sun, 27 Aug 2023 10:59:25 +0530 Subject: [PATCH 02/38] docs: fix formatting in readme --- readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index 01c6a15..2c34127 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -#
`RateLimit` Header Parser
+#
RateLimit Header Parser
@@ -6,6 +6,8 @@ [![npm version](https://img.shields.io/npm/v/ratelimit-header-parser.svg)](https://npmjs.org/package/ratelimit-header-parser 'View this project on NPM') [![npm downloads](https://img.shields.io/npm/dm/ratelimit-header-parser)](https://www.npmjs.com/package/ratelimit-header-parser) +
+ This library parses `RateLimit` headers of various forms into a normalized format. It supports the combined format specified in [draft 7](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers-07) @@ -14,8 +16,6 @@ of the the uncombined `RateLimit-*` format of earlier drafts, traditional `X-RateLimit-*` headers, and a few other formats. - - ## Usage ```ts From d045b3a1e198fadaf61a6c24150cd68b4781ddaa Mon Sep 17 00:00:00 2001 From: Vedant K Date: Sun, 27 Aug 2023 11:04:00 +0530 Subject: [PATCH 03/38] ci: add `publish` step --- .github/workflows/ci.yaml | 73 +++++++++++++++++++++++++++++++++++++++ .github/workflows/ci.yml | 46 ------------------------ 2 files changed, 73 insertions(+), 46 deletions(-) create mode 100644 .github/workflows/ci.yaml delete mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..e48f2e5 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,73 @@ +# /.github/workflows/ci.yaml +# GitHub actions workflow + +name: CI + +on: [push, pull_request] + +jobs: + lint: + name: Lint + strategy: + matrix: + node-version: [lts/*] + os: [ubuntu-latest] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout the repository + uses: actions/checkout@v3 + - name: Use Node ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Lint code + run: | + npm ci + npm run lint + test-library: + name: Test (Library) + strategy: + matrix: + node-version: [lts/gallium, lts/*, latest] + os: [ubuntu-latest, windows-latest, macos-latest] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout the repository + uses: actions/checkout@v3 + - name: Use Node ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Run tests + run: | + npm ci + npm run test:lib + publish: + name: Publish + needs: [lint, test-library] + if: startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + steps: + - name: Checkout the repository + uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: lts/* + registry-url: https://registry.npmjs.org/ + - name: Install dependencies + run: npm ci + - name: Publish to npm + run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Build package to upload to GitHub releases + run: | + npm pack + mv rratelimit-header-parser-*.tgz ratelimit-header-parser.tgz + - name: Create a Github release + uses: softprops/action-gh-release@v1 + with: + files: ratelimit-header-parser.tgz + body: + You can view the changelog + [here](https://github.com/express-rate-limit/ratelimit-header-parser/blob/main/changelog.md). diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index db38438..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,46 +0,0 @@ -# /.github/workflows/ci.yaml -# GitHub actions workflow - -name: CI - -on: [push, pull_request] - -permissions: - contents: read - -jobs: - lint: - name: Lint - strategy: - matrix: - node-version: [lts/*] - os: [ubuntu-latest] - runs-on: ${{ matrix.os }} - steps: - - name: Checkout the repository - uses: actions/checkout@v3 - - name: Use Node ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - - name: Check for lint/formatting errors - run: | - npm ci - npm run lint - test-node: - name: Test in node.js - strategy: - matrix: - node-version: [16, lts/*, latest] - runs-on: ubuntu-latest - steps: - - name: Checkout the repository - uses: actions/checkout@v3 - - name: Use Node ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - - name: Run library tests - run: | - npm ci - npm run test:lib From a137b2083e287920522e7fa9b45d19ddc0370e82 Mon Sep 17 00:00:00 2001 From: Vedant K Date: Sun, 27 Aug 2023 12:26:01 +0530 Subject: [PATCH 04/38] refc: rename and move some types around, add comments --- source/index.ts | 6 ++ source/ratelimit-header-parser.ts | 99 +++++++++++++++++-------------- source/types.ts | 59 ++++++++++++++++-- 3 files changed, 115 insertions(+), 49 deletions(-) diff --git a/source/index.ts b/source/index.ts index 0e87867..fab3f14 100644 --- a/source/index.ts +++ b/source/index.ts @@ -1,2 +1,8 @@ +// /source/index.ts +// Export away! + +// Export all the types as named exports export * from './types.js' + +// Export the public API as named exports too. export { parseRateLimit } from './ratelimit-header-parser.js' diff --git a/source/ratelimit-header-parser.ts b/source/ratelimit-header-parser.ts index a75ed5e..523d1fa 100644 --- a/source/ratelimit-header-parser.ts +++ b/source/ratelimit-header-parser.ts @@ -1,51 +1,60 @@ -import type { - ServerResponse, - IncomingHttpHeaders, - OutgoingHttpHeaders, -} from 'node:http' -import type { RateLimit, RateLimitOptions } from './types' - -// Node or fetch -export type ResponseObject = ServerResponse | Response -export type HeadersObject = - | IncomingHttpHeaders - | OutgoingHttpHeaders - | Headers - | { [key: string]: string | string[] } -export type ResponseOrHeadersObject = ResponseObject | HeadersObject +// /source/ratelimit-header-parser.ts +// The parser and associated functions +import type { + ResponseObject, + HeadersObject, + RateLimitInfo, + ParserOptions, +} from './types' + +/** + * Parses the passed response/headers object and returns rate limit information. + * + * @param input {ResponseObject | HeadersObject} - The node/fetch-style response/headers object. + * @param passedOptions {Partial | undefined} - The configuration for the parser. + * + * @returns {RateLimitInfo | undefined} - The rate limit information parsed from the headers. + */ export function parseRateLimit( - input: ResponseOrHeadersObject, - options?: RateLimitOptions, -): RateLimit | undefined { + input: ResponseObject | HeadersObject, + passedOptions?: Partial, +): RateLimitInfo | undefined { + // Default to no configuration. + const options = passedOptions ?? {} + + // Get the headers object from the passed input. + let headers: HeadersObject if ( 'headers' in input && typeof input.headers === 'object' && !Array.isArray(input.headers) - ) { - return parseHeadersObject(input.headers, options) - } - - if ('getHeaders' in input && typeof input.getHeaders === 'function') { - return parseHeadersObject(input.getHeaders(), options) - } - - return parseHeadersObject(input as HeadersObject, options) + ) + headers = input.headers + else if ('getHeaders' in input && typeof input.getHeaders === 'function') + headers = input.getHeaders() + else headers = input as HeadersObject + + // Parse the headers. + return parseHeaders(headers, options) } -function parseHeadersObject( - input: HeadersObject, - options: RateLimitOptions | undefined, -): RateLimit | undefined { - const combined = getHeader(input, 'ratelimit') +/** + * The internal parser function. + */ +function parseHeaders( + headers: HeadersObject, + options: Partial, +): RateLimitInfo | undefined { + const combined = getHeader(headers, 'ratelimit') if (combined) return parseCombinedRateLimitHeader(combined) let prefix - if (getHeader(input, 'ratelimit-remaining')) { + if (getHeader(headers, 'ratelimit-remaining')) { prefix = 'ratelimit-' - } else if (getHeader(input, 'x-ratelimit-remaining')) { + } else if (getHeader(headers, 'x-ratelimit-remaining')) { prefix = 'x-ratelimit-' - } else if (getHeader(input, 'x-rate-limit-remaining')) { + } else if (getHeader(headers, 'x-rate-limit-remaining')) { // Twitter - https://developer.twitter.com/en/docs/twitter-api/rate-limits#headers-and-codes prefix = 'x-rate-limit-' } else { @@ -57,18 +66,18 @@ function parseHeadersObject( return } - const limit = toInt(getHeader(input, `${prefix}limit`)) + const limit = toInt(getHeader(headers, `${prefix}limit`)) // Used - https://github.com/reddit-archive/reddit/wiki/API#rules // used - https://docs.github.com/en/rest/overview/resources-in-the-rest-api?apiVersion=2022-11-28#rate-limit-headers // observed - https://docs.gitlab.com/ee/administration/settings/user_and_ip_rate_limits.html#response-headers // note that || is valid here because used should always be at least 1, and || handles NaN correctly, whereas ?? doesn't const used = - toInt(getHeader(input, `${prefix}used`)) || - toInt(getHeader(input, `${prefix}observed`)) - const remaining = toInt(getHeader(input, `${prefix}remaining`)) + toInt(getHeader(headers, `${prefix}used`)) || + toInt(getHeader(headers, `${prefix}observed`)) + const remaining = toInt(getHeader(headers, `${prefix}remaining`)) let reset: Date | undefined - const resetRaw = getHeader(input, `${prefix}reset`) + const resetRaw = getHeader(headers, `${prefix}reset`) const resetType = options?.reset switch (resetType) { case 'date': { @@ -95,7 +104,7 @@ function parseHeadersObject( if (resetRaw) reset = parseResetAuto(resetRaw) else { // Fallback to retry-after - const retryAfter = getHeader(input, 'retry-after') + const retryAfter = getHeader(headers, 'retry-after') if (retryAfter) { reset = parseResetUnix(retryAfter) } @@ -114,10 +123,10 @@ function parseHeadersObject( const reLimit = /limit\s*=\s*(\d+)/i const reRemaining = /remaining\s*=\s*(\d+)/i const reReset = /reset\s*=\s*(\d+)/i -export function parseCombinedRateLimitHeader(input: string): RateLimit { - const limit = toInt(reLimit.exec(input)?.[1]) - const remaining = toInt(reRemaining.exec(input)?.[1]) - const resetSeconds = toInt(reReset.exec(input)?.[1]) +export function parseCombinedRateLimitHeader(header: string): RateLimitInfo { + const limit = toInt(reLimit.exec(header)?.[1]) + const remaining = toInt(reRemaining.exec(header)?.[1]) + const resetSeconds = toInt(reReset.exec(header)?.[1]) const reset = secondsToDate(resetSeconds) return { limit, diff --git a/source/types.ts b/source/types.ts index 1e9c39f..f37caba 100644 --- a/source/types.ts +++ b/source/types.ts @@ -1,11 +1,62 @@ -export type RateLimit = { +// /source/types.ts +// All the types used by this package + +import type { + ServerResponse, + IncomingHttpHeaders, + OutgoingHttpHeaders, +} from 'node:http' + +/** + * The parser accepts node/fetch style response and header objects. + */ +export type ResponseObject = ServerResponse | Response +export type HeadersObject = + | IncomingHttpHeaders + | OutgoingHttpHeaders + | Headers + | { [key: string]: string | string[] } + +/** + * The rate limit information gleaned from the response/headers object passed + * to the parser. + */ +export type RateLimitInfo = { + /** + * The max number of requests one can make to that endpoint in the stipulated + * window. + */ limit: number + + /** + * The number of requests already made to that endpoint. + */ used: number + + /** + * The number of requests that can be made before reaching the rate limit. + */ remaining: number + + /** + * The timestamp at which the window resets, and one's hit count is set to zero. + */ reset?: Date - // Todo: policy + + // TODO: policy } -export type RateLimitOptions = { - reset?: 'date' | 'unix' | 'seconds' | 'milliseconds' +/** + * Options that configure how the library parses the headers. + */ +export type ParserOptions = { + /** + * How to parse the `reset` field. If unset, the parser will guess based on + * the content of the header. + */ + reset: + | 'date' // Pass the value to `new Date(...)` to let the JavaScript engine parse it. + | 'unix' // Treat the value as the number of seconds since January 1, 1970 (A.K.A a UNIX epoch timestamp). + | 'seconds' // Treat the value as the number of seconds from the current time. + | 'milliseconds' // Treat the value as the number of milliseconds from the current time. } From 2bb00727f394e76bd2bc45060ec88e46e914dbf9 Mon Sep 17 00:00:00 2001 From: Vedant K Date: Sun, 27 Aug 2023 12:36:27 +0530 Subject: [PATCH 05/38] refc: extract some functions into a `utilities.ts` file --- source/ratelimit-header-parser.ts | 12 +----------- source/utilities.ts | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 source/utilities.ts diff --git a/source/ratelimit-header-parser.ts b/source/ratelimit-header-parser.ts index 523d1fa..a16ab49 100644 --- a/source/ratelimit-header-parser.ts +++ b/source/ratelimit-header-parser.ts @@ -7,6 +7,7 @@ import type { RateLimitInfo, ParserOptions, } from './types' +import { secondsToDate, toInt } from './utilities.js' /** * Parses the passed response/headers object and returns rate limit information. @@ -136,17 +137,6 @@ export function parseCombinedRateLimitHeader(header: string): RateLimitInfo { } } -function secondsToDate(seconds: number): Date { - const d = new Date() - d.setSeconds(d.getSeconds() + seconds) - return d -} - -function toInt(input: string | number | undefined): number { - if (typeof input === 'number') return input - return Number.parseInt(input ?? '', 10) -} - function getHeader(headers: HeadersObject, name: string): string | undefined { if ('get' in headers && typeof headers.get === 'function') { return headers.get(name) ?? undefined // Returns null if missing, but everything else is undefined for missing values diff --git a/source/utilities.ts b/source/utilities.ts new file mode 100644 index 0000000..c130923 --- /dev/null +++ b/source/utilities.ts @@ -0,0 +1,28 @@ +// source/utilities.ts +// The utility functions for the library + +/** + * Adds the given number of seconds to the current time and returns a `Date`. + * + * @param seconds {number} - The number of seconds to add to the current time. + * + * @return {Date} - The date, with a timestamp n seconds from now. + */ +export const secondsToDate = (seconds: number): Date => { + const date = new Date() + date.setSeconds(date.getSeconds() + seconds) + return date +} + +/** + * Converts a string/number to a number. + * + * @param input {string | number | undefined} - The input to convert to a number. + * + * @return {number} - The parsed integer. + * @throws {Error} - Thrown if the string does not contain a valid number. + */ +export const toInt = (input: string | number | undefined): number => { + if (typeof input === 'number') return input + return Number.parseInt(input ?? '', 10) +} From 351f405dc6e9c2b5be55a7336627fb388a1267c1 Mon Sep 17 00:00:00 2001 From: Vedant K Date: Sun, 27 Aug 2023 12:47:31 +0530 Subject: [PATCH 06/38] chore: fix formatting --- source/utilities.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/utilities.ts b/source/utilities.ts index c130923..c76403b 100644 --- a/source/utilities.ts +++ b/source/utilities.ts @@ -1,4 +1,4 @@ -// source/utilities.ts +// /source/utilities.ts // The utility functions for the library /** From 146610aa957e66296233810c3bd093a99da5fcb1 Mon Sep 17 00:00:00 2001 From: Vedant K Date: Sun, 27 Aug 2023 13:09:19 +0530 Subject: [PATCH 07/38] build: use `pkgroll` to bundle library --- .gitattributes | 4 + .npmrc | 6 + .prettierignore | 3 + config/jest.json | 12 + package-lock.json | 2975 +++++++++++++++++++++++++++++++-------------- package.json | 104 +- tsconfig.json | 2 +- 7 files changed, 2142 insertions(+), 964 deletions(-) create mode 100644 .gitattributes create mode 100644 .npmrc create mode 100644 config/jest.json diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2c3de85 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +# /.gitattributes +# Makes sure all line endings are LF + +* text=auto eol=lf diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..318a75f --- /dev/null +++ b/.npmrc @@ -0,0 +1,6 @@ +# .npmrc +# Configuration for npm and pnpm + +# Uses the exact version instead of any within-patch-range version of an +# installed package +save-exact=true diff --git a/.prettierignore b/.prettierignore index 80aa938..2dd5704 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,6 @@ +# /.prettierignore +# Tells Prettier to ignore certain files + package.json package-lock.json diff --git a/config/jest.json b/config/jest.json new file mode 100644 index 0000000..eeed9e0 --- /dev/null +++ b/config/jest.json @@ -0,0 +1,12 @@ +{ + "rootDir": "../", + "preset": "ts-jest/presets/default-esm", + "collectCoverage": true, + "collectCoverageFrom": ["source/**/*.ts"], + "testTimeout": 30000, + "testMatch": ["**/test/*-test.ts"], + "moduleFileExtensions": ["js", "jsx", "json", "ts", "tsx"], + "moduleNameMapper": { + "^(\\.{1,2}/.*)\\.js$": "$1" + } +} diff --git a/package-lock.json b/package-lock.json index 88baaa5..7414ccf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,9 +21,14 @@ "jest": "^29.6.3", "lint-staged": "^14.0.1", "npm-run-all": "^4.1.5", + "pkgroll": "^1.11.0", "ts-jest": "^29.1.1", + "tsx": "^3.12.7", "typescript": "^5.1.6", "xo": "^0.56.0" + }, + "engines": { + "node": ">= 16" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -676,10 +681,30 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, - "node_modules/@esbuild/android-arm": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.2.tgz", - "integrity": "sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q==", + "node_modules/@esbuild-kit/cjs-loader": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@esbuild-kit/cjs-loader/-/cjs-loader-2.4.2.tgz", + "integrity": "sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==", + "dev": true, + "dependencies": { + "@esbuild-kit/core-utils": "^3.0.0", + "get-tsconfig": "^4.4.0" + } + }, + "node_modules/@esbuild-kit/core-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.1.0.tgz", + "integrity": "sha512-Uuk8RpCg/7fdHSceR1M6XbSZFSuMrxcePFuGgyvsBn+u339dk5OeL4jv2EojwTN2st/unJGsVm4qHWjWNmJ/tw==", + "dev": true, + "dependencies": { + "esbuild": "~0.17.6", + "source-map-support": "^0.5.21" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", + "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", "cpu": [ "arm" ], @@ -692,10 +717,10 @@ "node": ">=12" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.2.tgz", - "integrity": "sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw==", + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", + "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", "cpu": [ "arm64" ], @@ -708,10 +733,10 @@ "node": ">=12" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.2.tgz", - "integrity": "sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w==", + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", + "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", "cpu": [ "x64" ], @@ -724,10 +749,10 @@ "node": ">=12" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.2.tgz", - "integrity": "sha512-Ora8JokrvrzEPEpZO18ZYXkH4asCdc1DLdcVy8TGf5eWtPO1Ie4WroEJzwI52ZGtpODy3+m0a2yEX9l+KUn0tA==", + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", + "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", "cpu": [ "arm64" ], @@ -740,10 +765,10 @@ "node": ">=12" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.2.tgz", - "integrity": "sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw==", + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", + "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", "cpu": [ "x64" ], @@ -756,10 +781,10 @@ "node": ">=12" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.2.tgz", - "integrity": "sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ==", + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", + "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", "cpu": [ "arm64" ], @@ -772,10 +797,10 @@ "node": ">=12" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.2.tgz", - "integrity": "sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw==", + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", + "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", "cpu": [ "x64" ], @@ -788,10 +813,10 @@ "node": ">=12" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.2.tgz", - "integrity": "sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg==", + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", + "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", "cpu": [ "arm" ], @@ -804,10 +829,10 @@ "node": ">=12" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.2.tgz", - "integrity": "sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg==", + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", + "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", "cpu": [ "arm64" ], @@ -820,10 +845,10 @@ "node": ">=12" } }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.2.tgz", - "integrity": "sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ==", + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", + "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", "cpu": [ "ia32" ], @@ -836,10 +861,10 @@ "node": ">=12" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.2.tgz", - "integrity": "sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw==", + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-loong64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", + "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", "cpu": [ "loong64" ], @@ -852,10 +877,10 @@ "node": ">=12" } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.2.tgz", - "integrity": "sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg==", + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-mips64el": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", + "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", "cpu": [ "mips64el" ], @@ -868,10 +893,10 @@ "node": ">=12" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.2.tgz", - "integrity": "sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw==", + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ppc64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", + "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", "cpu": [ "ppc64" ], @@ -884,10 +909,10 @@ "node": ">=12" } }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.2.tgz", - "integrity": "sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw==", + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-riscv64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", + "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", "cpu": [ "riscv64" ], @@ -900,10 +925,10 @@ "node": ">=12" } }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.2.tgz", - "integrity": "sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g==", + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-s390x": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", + "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", "cpu": [ "s390x" ], @@ -916,10 +941,10 @@ "node": ">=12" } }, - "node_modules/@esbuild/linux-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.2.tgz", - "integrity": "sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ==", + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", + "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", "cpu": [ "x64" ], @@ -932,10 +957,10 @@ "node": ">=12" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.2.tgz", - "integrity": "sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ==", + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/netbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", + "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", "cpu": [ "x64" ], @@ -948,10 +973,10 @@ "node": ">=12" } }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.2.tgz", - "integrity": "sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw==", + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/openbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", + "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", "cpu": [ "x64" ], @@ -964,10 +989,10 @@ "node": ">=12" } }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.2.tgz", - "integrity": "sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw==", + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/sunos-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", + "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", "cpu": [ "x64" ], @@ -980,10 +1005,10 @@ "node": ">=12" } }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.2.tgz", - "integrity": "sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg==", + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", + "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", "cpu": [ "arm64" ], @@ -996,10 +1021,10 @@ "node": ">=12" } }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.2.tgz", - "integrity": "sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA==", + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", + "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", "cpu": [ "ia32" ], @@ -1012,10 +1037,10 @@ "node": ">=12" } }, - "node_modules/@esbuild/win32-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.2.tgz", - "integrity": "sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw==", + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", + "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", "cpu": [ "x64" ], @@ -1028,116 +1053,525 @@ "node": ">=12" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "node_modules/@esbuild-kit/core-utils/node_modules/esbuild": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", + "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=12" }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "optionalDependencies": { + "@esbuild/android-arm": "0.17.19", + "@esbuild/android-arm64": "0.17.19", + "@esbuild/android-x64": "0.17.19", + "@esbuild/darwin-arm64": "0.17.19", + "@esbuild/darwin-x64": "0.17.19", + "@esbuild/freebsd-arm64": "0.17.19", + "@esbuild/freebsd-x64": "0.17.19", + "@esbuild/linux-arm": "0.17.19", + "@esbuild/linux-arm64": "0.17.19", + "@esbuild/linux-ia32": "0.17.19", + "@esbuild/linux-loong64": "0.17.19", + "@esbuild/linux-mips64el": "0.17.19", + "@esbuild/linux-ppc64": "0.17.19", + "@esbuild/linux-riscv64": "0.17.19", + "@esbuild/linux-s390x": "0.17.19", + "@esbuild/linux-x64": "0.17.19", + "@esbuild/netbsd-x64": "0.17.19", + "@esbuild/openbsd-x64": "0.17.19", + "@esbuild/sunos-x64": "0.17.19", + "@esbuild/win32-arm64": "0.17.19", + "@esbuild/win32-ia32": "0.17.19", + "@esbuild/win32-x64": "0.17.19" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.7.0.tgz", - "integrity": "sha512-+HencqxU7CFJnQb7IKtuNBqS6Yx3Tz4kOL8BJXo+JyeiBm5MEX6pO8onXDkjrkCRlfYXS1Axro15ZjVFe9YgsA==", + "node_modules/@esbuild-kit/esm-loader": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.5.5.tgz", + "integrity": "sha512-Qwfvj/qoPbClxCRNuac1Du01r9gvNOT+pMYtJDapfB1eoGN1YlJ1BixLyL9WVENRx5RXgNLdfYdx/CuswlGhMw==", "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "dependencies": { + "@esbuild-kit/core-utils": "^3.0.0", + "get-tsconfig": "^4.4.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", - "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "node_modules/@esbuild/android-arm": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.2.tgz", + "integrity": "sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q==", + "cpu": [ + "arm" + ], "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=12" } }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.21.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", - "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "node_modules/@esbuild/android-arm64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.2.tgz", + "integrity": "sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12" } }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/@esbuild/android-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.2.tgz", + "integrity": "sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.2.tgz", + "integrity": "sha512-Ora8JokrvrzEPEpZO18ZYXkH4asCdc1DLdcVy8TGf5eWtPO1Ie4WroEJzwI52ZGtpODy3+m0a2yEX9l+KUn0tA==", + "cpu": [ + "arm64" + ], "dev": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12" } }, - "node_modules/@eslint/js": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", - "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.2.tgz", + "integrity": "sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=12" } }, - "node_modules/@express-rate-limit/prettier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@express-rate-limit/prettier/-/prettier-1.0.0.tgz", - "integrity": "sha512-d69iHtmIVtfgMq279yTclYteZn4uv5m9wo4MEE/tfgWUc5Qjc4O4tfeB+W9ztNkv5kGStiQeTT3LZw0m4QjwGg==", - "dev": true - }, - "node_modules/@express-rate-limit/tsconfig": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@express-rate-limit/tsconfig/-/tsconfig-1.0.0.tgz", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.2.tgz", + "integrity": "sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.2.tgz", + "integrity": "sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.2.tgz", + "integrity": "sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.2.tgz", + "integrity": "sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.2.tgz", + "integrity": "sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.2.tgz", + "integrity": "sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.2.tgz", + "integrity": "sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.2.tgz", + "integrity": "sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.2.tgz", + "integrity": "sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.2.tgz", + "integrity": "sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.2.tgz", + "integrity": "sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.2.tgz", + "integrity": "sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.2.tgz", + "integrity": "sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.2.tgz", + "integrity": "sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.2.tgz", + "integrity": "sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.2.tgz", + "integrity": "sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.2.tgz", + "integrity": "sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.7.0.tgz", + "integrity": "sha512-+HencqxU7CFJnQb7IKtuNBqS6Yx3Tz4kOL8BJXo+JyeiBm5MEX6pO8onXDkjrkCRlfYXS1Axro15ZjVFe9YgsA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", + "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@express-rate-limit/prettier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@express-rate-limit/prettier/-/prettier-1.0.0.tgz", + "integrity": "sha512-d69iHtmIVtfgMq279yTclYteZn4uv5m9wo4MEE/tfgWUc5Qjc4O4tfeB+W9ztNkv5kGStiQeTT3LZw0m4QjwGg==", + "dev": true + }, + "node_modules/@express-rate-limit/tsconfig": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@express-rate-limit/tsconfig/-/tsconfig-1.0.0.tgz", "integrity": "sha512-4QmlmxgFVlyxot8h6W2VO681vFsYq7jxP3kDtBEHQAljOVxlpDWPTruva3ulSh6qQdWFGA8mNka82C5xkr//cg==", "dev": true }, @@ -1597,16 +2031,259 @@ "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", "dev": true, "dependencies": { - "default-browser": "^4.0.0", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@rollup/plugin-alias": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-4.0.4.tgz", + "integrity": "sha512-0CaAY238SMtYAWEXXptWSR8iz8NYZnH7zNBKuJ14xFJSGwLtPgjvXYsoApAHfzYXXH1ejxpVw7WlHss3zhh9SQ==", + "dev": true, + "dependencies": { + "slash": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-alias/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "24.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-24.1.0.tgz", + "integrity": "sha512-eSL45hjhCWI0jCCXcNtLVqM5N1JlBGvlFfY0m6oOYnLCJ6N0qEXoZql4sY2MOUArzhH4SA/qBpTxvvZp2Sc+DQ==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "glob": "^8.0.3", + "is-reference": "1.2.1", + "magic-string": "^0.27.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/magic-string": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", + "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.13" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@rollup/plugin-inject": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-inject/-/plugin-inject-5.0.3.tgz", + "integrity": "sha512-411QlbL+z2yXpRWFXSmw/teQRMkXcAAC8aYTemc15gwJRpvEVDQwoe+N/HTFD8RFG8+88Bme9DK2V9CVm7hJdA==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "estree-walker": "^2.0.2", + "magic-string": "^0.27.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-inject/node_modules/magic-string": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", + "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.13" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@rollup/plugin-json": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.0.0.tgz", + "integrity": "sha512-i/4C5Jrdr1XUarRhVu27EEwjt4GObltD7c+MkCIpO2QIbojw8MUs+CCTqOphQi3Qtg1FLmYt+l+6YeoIf51J7w==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.1.tgz", + "integrity": "sha512-nsbUg588+GDSu8/NS8T4UAshO6xeaOfINNuXeVHcKV02LJtoRaM1SiOacClw4kws1SFiNhdLGxlbMY9ga/zs/w==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-builtin-module": "^3.2.1", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-replace": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-5.0.2.tgz", + "integrity": "sha512-M9YXNekv/C/iHHK+cvORzfRYfPbq0RDD8r0G+bMiTXjNGKulPnCT9O3Ss46WfhI6ZOCgApOP7xAdmCQJ+U2LAA==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "magic-string": "^0.27.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-replace/node_modules/magic-string": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", + "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.13" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.4.tgz", + "integrity": "sha512-0KJnIoRI8A+a1dqOYLxH8vBf8bphDmty5QvIm2hqm7oFCFYKCAZWWd2hXgMibaPsNDhI0AtpYfQZJG47pt/k4g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" }, "engines": { - "node": ">=14.16" + "node": ">=14.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } } }, "node_modules/@sinclair/typebox": { @@ -1764,6 +2441,12 @@ "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", "dev": true }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true + }, "node_modules/@types/semver": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", @@ -3172,6 +3855,12 @@ "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", "dev": true }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -4800,6 +5489,12 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -5904,6 +6599,12 @@ "js-types": "^1.0.0" } }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true + }, "node_modules/is-negated-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", @@ -6002,6 +6703,15 @@ "proto-props": "^2.0.0" } }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -7250,7 +7960,231 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/listr2/node_modules/wrap-ansi": { + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz", + "integrity": "sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==", + "dev": true, + "dependencies": { + "ansi-escapes": "^5.0.0", + "cli-cursor": "^4.0.0", + "slice-ansi": "^5.0.0", + "strip-ansi": "^7.0.1", + "wrap-ansi": "^8.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", + "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", + "dev": true, + "dependencies": { + "type-fest": "^1.0.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/log-update/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", @@ -7267,116 +8201,144 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/load-json-file/node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.3.tgz", + "integrity": "sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" }, "engines": { - "node": ">=4" + "node": ">=12" } }, - "node_modules/load-json-file/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "node_modules/make-dir/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, - "peer": true, + "dependencies": { + "yallist": "^4.0.0" + }, "engines": { - "node": ">=6.11.5" + "node": ">=10" } }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/make-dir/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { - "p-locate": "^4.1.0" + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "dev": true - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "node_modules/make-dir/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, + "tmpl": "1.0.5" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz", - "integrity": "sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==", + "node_modules/memory-fs": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz", + "integrity": "sha512-+y4mDxU4rvXXu5UDSGCGNiesFmwCHuefGMoPCO1WYucNYj7DsLqrFaa2fXVI0H+NNiPTwwzKwspn9yTZqUGqng==", + "dev": true + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/meow": { + "version": "10.1.5", + "resolved": "https://registry.npmjs.org/meow/-/meow-10.1.5.tgz", + "integrity": "sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw==", "dev": true, "dependencies": { - "ansi-escapes": "^5.0.0", - "cli-cursor": "^4.0.0", - "slice-ansi": "^5.0.0", - "strip-ansi": "^7.0.1", - "wrap-ansi": "^8.0.1" + "@types/minimist": "^1.2.2", + "camelcase-keys": "^7.0.0", + "decamelize": "^5.0.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.2", + "read-pkg-up": "^8.0.0", + "redent": "^4.0.0", + "trim-newlines": "^4.0.2", + "type-fest": "^1.2.2", + "yargs-parser": "^20.2.9" }, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" @@ -7385,146 +8347,190 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update/node_modules/ansi-escapes": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", - "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", + "node_modules/meow/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", "dev": true, - "dependencies": { - "type-fest": "^1.0.2" - }, "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "node_modules/meow/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "node": ">=10" } }, - "node_modules/log-update/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">= 8" } }, - "node_modules/log-update/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "node_modules/micro-spelling-correcter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/micro-spelling-correcter/-/micro-spelling-correcter-1.1.1.tgz", + "integrity": "sha512-lkJ3Rj/mtjlRcHk6YyCbvZhyWTOzdBvTHsxMmZSk5jxN1YyVSQ+JETAom55mdzfcyDrY/49Z7UCW760BK30crg==", "dev": true }, - "node_modules/log-update/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "peer": true, + "dependencies": { + "mime-db": "1.52.0" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.6" } }, - "node_modules/log-update/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">=6" } }, - "node_modules/log-update/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": "*" } }, - "node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", "dev": true, "dependencies": { - "yallist": "^3.0.2" + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "peer": true + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "dev": true + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", "dev": true, "dependencies": { - "semver": "^7.5.3" + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-dir/node_modules/lru-cache": { + "node_modules/normalize-package-data/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", @@ -7536,7 +8542,7 @@ "node": ">=10" } }, - "node_modules/make-dir/node_modules/semver": { + "node_modules/normalize-package-data/node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", @@ -7551,835 +8557,972 @@ "node": ">=10" } }, - "node_modules/make-dir/node_modules/yallist": { + "node_modules/normalize-package-data/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", "dev": true, "dependencies": { - "tmpl": "1.0.5" + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": ">= 4" } }, - "node_modules/map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "node_modules/npm-run-all/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, "engines": { - "node": ">=8" + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=4" } }, - "node_modules/memory-fs": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz", - "integrity": "sha512-+y4mDxU4rvXXu5UDSGCGNiesFmwCHuefGMoPCO1WYucNYj7DsLqrFaa2fXVI0H+NNiPTwwzKwspn9yTZqUGqng==", + "node_modules/npm-run-all/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/npm-run-all/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, - "node_modules/memorystream": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "node_modules/npm-run-all/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/npm-run-all/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/npm-run-all/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true, "engines": { - "node": ">= 0.10.0" + "node": ">=4" } }, - "node_modules/meow": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/meow/-/meow-10.1.5.tgz", - "integrity": "sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw==", + "node_modules/npm-run-all/node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", "dev": true, - "dependencies": { - "@types/minimist": "^1.2.2", - "camelcase-keys": "^7.0.0", - "decamelize": "^5.0.0", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.2", - "read-pkg-up": "^8.0.0", - "redent": "^4.0.0", - "trim-newlines": "^4.0.2", - "type-fest": "^1.2.2", - "yargs-parser": "^20.2.9" + "bin": { + "pidtree": "bin/pidtree.js" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10" } }, - "node_modules/meow/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "node_modules/npm-run-all/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "bin": { + "semver": "bin/semver" } }, - "node_modules/meow/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "node_modules/npm-run-all/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "node_modules/npm-run-all/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "dev": true, "engines": { - "node": ">= 8" + "node": ">=0.10.0" } }, - "node_modules/micro-spelling-correcter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/micro-spelling-correcter/-/micro-spelling-correcter-1.1.1.tgz", - "integrity": "sha512-lkJ3Rj/mtjlRcHk6YyCbvZhyWTOzdBvTHsxMmZSk5jxN1YyVSQ+JETAom55mdzfcyDrY/49Z7UCW760BK30crg==", - "dev": true - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "node_modules/npm-run-all/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" + "has-flag": "^3.0.0" }, "engines": { - "node": ">=8.6" + "node": ">=4" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "node_modules/npm-run-all/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, - "peer": true, - "engines": { - "node": ">= 0.6" + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, - "peer": true, "dependencies": { - "mime-db": "1.52.0" + "path-key": "^3.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">=8" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "node_modules/obj-props": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/obj-props/-/obj-props-1.4.0.tgz", + "integrity": "sha512-p7p/7ltzPDiBs6DqxOrIbtRdwxxVRBj5ROukeNb9RgA+fawhrz5n2hpNz8DDmYR//tviJSj7nUnlppGmONkjiQ==", "dev": true, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "dev": true, - "engines": { - "node": ">=4" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, "engines": { - "node": "*" + "node": ">= 0.4" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "node_modules/object.values": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", "dev": true, "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "engines": { - "node": ">= 6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/natural-compare": { + "node_modules/once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "peer": true - }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", - "dev": true - }, - "node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" }, "engines": { - "node": ">=10" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/normalize-package-data/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", "dev": true, "dependencies": { - "yallist": "^4.0.0" + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" }, "engines": { - "node": ">=10" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/open-editor": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/open-editor/-/open-editor-4.1.0.tgz", + "integrity": "sha512-uQwuSQrayyIakkhJHtQZMSSXGpWmuNXa0enDhzQ+MeOrU7us2xIU1T4T0vFpssRUR8iSUC7r7F/H2Qi9I7iKRw==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "env-editor": "^1.1.0", + "execa": "^5.1.1", + "line-column-path": "^3.0.0", + "open": "^8.4.0" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/normalize-package-data/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/open/node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/npm-run-all": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", - "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "dependencies": { - "ansi-styles": "^3.2.1", - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "memorystream": "^0.3.1", - "minimatch": "^3.0.4", - "pidtree": "^0.3.0", - "read-pkg": "^3.0.0", - "shell-quote": "^1.6.1", - "string.prototype.padend": "^3.0.0" - }, - "bin": { - "npm-run-all": "bin/npm-run-all/index.js", - "run-p": "bin/run-p/index.js", - "run-s": "bin/run-s/index.js" + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" }, "engines": { - "node": ">= 4" + "node": ">= 0.8.0" } }, - "node_modules/npm-run-all/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { - "color-convert": "^1.9.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm-run-all/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "p-limit": "^2.2.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/npm-run-all/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "dependencies": { - "color-name": "1.1.3" + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm-run-all/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/npm-run-all/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "node_modules/p-map": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-5.5.0.tgz", + "integrity": "sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==", "dev": true, "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "aggregate-error": "^4.0.0" }, "engines": { - "node": ">=4.8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm-run-all/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, "engines": { - "node": ">=0.8.0" + "node": ">=6" } }, - "node_modules/npm-run-all/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, "engines": { - "node": ">=4" + "node": ">=6" } }, - "node_modules/npm-run-all/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm-run-all/node_modules/pidtree": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", - "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "bin": { - "pidtree": "bin/pidtree.js" - }, "engines": { - "node": ">=0.10" + "node": ">=8" } }, - "node_modules/npm-run-all/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, - "bin": { - "semver": "bin/semver" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/npm-run-all/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, - "dependencies": { - "shebang-regex": "^1.0.0" - }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/npm-run-all/node_modules/shebang-regex": { + "node_modules/picocolors": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true }, - "node_modules/npm-run-all/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-all/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" + "node": ">=8.6" }, - "bin": { - "which": "bin/which" + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", "dev": true, - "dependencies": { - "path-key": "^3.0.0" + "bin": { + "pidtree": "bin/pidtree.js" }, "engines": { - "node": ">=8" + "node": ">=0.10" } }, - "node_modules/obj-props": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/obj-props/-/obj-props-1.4.0.tgz", - "integrity": "sha512-p7p/7ltzPDiBs6DqxOrIbtRdwxxVRBj5ROukeNb9RgA+fawhrz5n2hpNz8DDmYR//tviJSj7nUnlppGmONkjiQ==", + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true, "engines": { - "node": ">= 0.4" + "node": ">= 6" } }, - "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" + "find-up": "^4.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "node_modules/pkgroll": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/pkgroll/-/pkgroll-1.11.0.tgz", + "integrity": "sha512-E+RJuiqun1QbsGzkxwRV1xmdwsmDQ6uGOoLagLwS4b1YPFNAPESDqz5QQx/MxkZCinl07Tt3GXzMJQT+helsww==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "@rollup/plugin-alias": "^4.0.4", + "@rollup/plugin-commonjs": "^24.1.0", + "@rollup/plugin-inject": "^5.0.3", + "@rollup/plugin-json": "^6.0.0", + "@rollup/plugin-node-resolve": "^15.1.0", + "@rollup/plugin-replace": "^5.0.2", + "@rollup/pluginutils": "^5.0.2", + "esbuild": "^0.18.17", + "magic-string": "^0.30.2", + "rollup": "^2.79.1" }, - "engines": { - "node": ">= 0.4" + "bin": { + "pkgroll": "dist/cli.js" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/privatenumber/pkgroll?sponsor=1" + }, + "peerDependencies": { + "typescript": "^4.1 || ^5.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "node_modules/pkgroll/node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], "dev": true, - "dependencies": { - "wrappy": "1" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "node_modules/pkgroll/node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12" } }, - "node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "node_modules/pkgroll/node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, + "optional": true, + "os": [ + "android" + ], "engines": { "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/open-editor": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/open-editor/-/open-editor-4.1.0.tgz", - "integrity": "sha512-uQwuSQrayyIakkhJHtQZMSSXGpWmuNXa0enDhzQ+MeOrU7us2xIU1T4T0vFpssRUR8iSUC7r7F/H2Qi9I7iKRw==", + "node_modules/pkgroll/node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "env-editor": "^1.1.0", - "execa": "^5.1.1", - "line-column-path": "^3.0.0", - "open": "^8.4.0" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12" } }, - "node_modules/open/node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "node_modules/pkgroll/node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "node_modules/pkgroll/node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">= 0.8.0" + "node": ">=12" } }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/pkgroll/node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12" + } + }, + "node_modules/pkgroll/node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/pkgroll/node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/pkgroll/node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12" } }, - "node_modules/p-map": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-5.5.0.tgz", - "integrity": "sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==", + "node_modules/pkgroll/node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], "dev": true, - "dependencies": { - "aggregate-error": "^4.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "node_modules/pkgroll/node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6" + "node": ">=12" } }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "node_modules/pkgroll/node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6" + "node": ">=12" } }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "node_modules/pkgroll/node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12" } }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "node_modules/pkgroll/node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "node_modules/pkgroll/node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "node_modules/pkgroll/node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "node_modules/pkgroll/node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/pkgroll/node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">=12" } }, - "node_modules/pidtree": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", - "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "node_modules/pkgroll/node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], "dev": true, - "bin": { - "pidtree": "bin/pidtree.js" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=0.10" + "node": ">=12" } }, - "node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "node_modules/pkgroll/node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], "dev": true, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=4" + "node": ">=12" } }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "node_modules/pkgroll/node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 6" + "node": ">=12" } }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "node_modules/pkgroll/node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", "dev": true, - "dependencies": { - "find-up": "^4.0.0" + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" } }, "node_modules/plur": { @@ -8884,6 +10027,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rollup": { + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/run-applescript": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", @@ -9718,6 +10876,23 @@ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true }, + "node_modules/tsx": { + "version": "3.12.7", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-3.12.7.tgz", + "integrity": "sha512-C2Ip+jPmqKd1GWVQDvz/Eyc6QJbGfE7NrR3fx5BpEHMZsEHoIxHL1j+lKdGobr8ovEyqeNkPLSKp6SCSOt7gmw==", + "dev": true, + "dependencies": { + "@esbuild-kit/cjs-loader": "^2.4.2", + "@esbuild-kit/core-utils": "^3.0.0", + "@esbuild-kit/esm-loader": "^2.5.5" + }, + "bin": { + "tsx": "dist/cli.js" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index 70f0628..5732bb3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,19 @@ { "name": "ratelimit-header-parser", "version": "0.1.0", - "description": "Parse RateLimit headers of various forms, including the combined form from draft 7 of the IETF standard, into a normalized format.", + "description": "Parse `RateLimit` headers of various forms, including the combined form from draft 7 of the IETF standard, into a normalized format.", + "author": "Nathan Friedly ", + "license": "MIT", + "homepage": "https://github.com/express-rate-limit/ratelimit-header-parser#readme", + "repository": "https://github.com/express-rate-limit/ratelimit-header-parser", + "keywords": [ + "ratelimit", + "x-ratelimit-limit", + "headers", + "parser", + "express-rate-limit", + "ratelimit-policy" + ], "type": "module", "exports": { ".": { @@ -26,78 +38,42 @@ "license.md", "changelog.md" ], + "engines": { + "node": ">= 16" + }, "scripts": { "clean": "del-cli dist/ coverage/ *.log *.tmp *.bak *.tgz", - "build:cjs": "esbuild --platform=node --bundle --target=es2019 --format=cjs --outfile=dist/index.cjs source/index.ts", - "build:esm": "esbuild --platform=node --bundle --target=es2019 --format=esm --outfile=dist/index.mjs source/index.ts", - "build:types": "dts-bundle-generator --out-file=dist/index.d.ts source/index.ts && cp dist/index.d.ts dist/index.d.cts && cp dist/index.d.ts dist/index.d.mts", - "compile": "run-s clean build:*", + "build": "pkgroll --src source/", + "compile": "run-s clean build", "lint:code": "xo", "lint:rest": "prettier --ignore-unknown --check .", "lint": "run-s lint:*", - "format:code": "npm run lint:code -- --fix", - "format:rest": "npm run lint:rest -- --write .", + "format:code": "xo --fix", + "format:rest": "prettier --ignore-unknown --write .", "format": "run-s format:*", - "test:lib": "jest", + "test:lib": "jest --config config/jest.json", "test": "run-s lint test:lib", "pre-commit": "lint-staged", "prepare": "run-s compile && husky install config/husky" }, - "repository": { - "type": "git", - "url": "git+https://github.com/express-rate-limit/ratelimit-header-parser.git" - }, - "keywords": [ - "ratelimit", - "x-ratelimit-limit", - "headers", - "parser", - "express-rate-limit", - "ratelimit-policy" - ], - "author": "Nathan Friedly ", - "license": "MIT", - "bugs": { - "url": "https://github.com/express-rate-limit/ratelimit-header-parser/issues" - }, - "homepage": "https://github.com/express-rate-limit/ratelimit-header-parser#readme", "devDependencies": { - "@express-rate-limit/prettier": "^1.0.0", - "@express-rate-limit/tsconfig": "^1.0.0", - "@jest/globals": "^29.6.3", - "@jest/types": "^29.6.3", - "@types/node": "^20.5.1", - "del-cli": "^5.0.0", - "dts-bundle-generator": "^8.0.1", - "esbuild": "^0.19.2", - "husky": "^8.0.3", - "jest": "^29.6.3", - "lint-staged": "^14.0.1", - "npm-run-all": "^4.1.5", - "ts-jest": "^29.1.1", - "typescript": "^5.1.6", - "xo": "^0.56.0" - }, - "jest": { - "preset": "ts-jest/presets/default-esm", - "collectCoverage": true, - "collectCoverageFrom": [ - "source/**/*.ts" - ], - "testTimeout": 30000, - "testMatch": [ - "**/test/*-test.ts" - ], - "moduleFileExtensions": [ - "js", - "jsx", - "json", - "ts", - "tsx" - ], - "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.js$": "$1" - } + "@express-rate-limit/prettier": "1.1.0", + "@express-rate-limit/tsconfig": "1.0.0", + "@jest/globals": "29.6.3", + "@jest/types": "29.6.3", + "@types/node": "20.5.1", + "del-cli": "5.0.0", + "dts-bundle-generator": "8.0.1", + "esbuild": "0.19.2", + "husky": "8.0.3", + "jest": "29.6.3", + "lint-staged": "14.0.1", + "npm-run-all": "4.1.5", + "pkgroll": "1.11.0", + "ts-jest": "29.1.1", + "tsx": "3.12.7", + "typescript": "5.1.6", + "xo": "0.56.0" }, "xo": { "prettier": true, @@ -116,7 +92,9 @@ } } ], - "ignores": ["examples/"] + "ignores": [ + "examples/" + ] }, "prettier": "@express-rate-limit/prettier", "lint-staged": { diff --git a/tsconfig.json b/tsconfig.json index 4ef3c2a..1f5feb3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,6 +3,6 @@ "exclude": ["node_modules/"], "extends": "@express-rate-limit/tsconfig", "compilerOptions": { - "target": "ES2019" + "target": "es2021" } } From 49489fefa820c84fd78b22b93d7d1a13776f6c9a Mon Sep 17 00:00:00 2001 From: Vedant K Date: Sun, 27 Aug 2023 13:10:47 +0530 Subject: [PATCH 08/38] chore: update `package-lock.json` --- package-lock.json | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7414ccf..dfd5e53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,23 +9,23 @@ "version": "0.1.0", "license": "MIT", "devDependencies": { - "@express-rate-limit/prettier": "^1.0.0", - "@express-rate-limit/tsconfig": "^1.0.0", - "@jest/globals": "^29.6.3", - "@jest/types": "^29.6.3", - "@types/node": "^20.5.1", - "del-cli": "^5.0.0", - "dts-bundle-generator": "^8.0.1", - "esbuild": "^0.19.2", - "husky": "^8.0.3", - "jest": "^29.6.3", - "lint-staged": "^14.0.1", - "npm-run-all": "^4.1.5", - "pkgroll": "^1.11.0", - "ts-jest": "^29.1.1", - "tsx": "^3.12.7", - "typescript": "^5.1.6", - "xo": "^0.56.0" + "@express-rate-limit/prettier": "1.1.0", + "@express-rate-limit/tsconfig": "1.0.0", + "@jest/globals": "29.6.3", + "@jest/types": "29.6.3", + "@types/node": "20.5.1", + "del-cli": "5.0.0", + "dts-bundle-generator": "8.0.1", + "esbuild": "0.19.2", + "husky": "8.0.3", + "jest": "29.6.3", + "lint-staged": "14.0.1", + "npm-run-all": "4.1.5", + "pkgroll": "1.11.0", + "ts-jest": "29.1.1", + "tsx": "3.12.7", + "typescript": "5.1.6", + "xo": "0.56.0" }, "engines": { "node": ">= 16" @@ -1564,9 +1564,9 @@ } }, "node_modules/@express-rate-limit/prettier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@express-rate-limit/prettier/-/prettier-1.0.0.tgz", - "integrity": "sha512-d69iHtmIVtfgMq279yTclYteZn4uv5m9wo4MEE/tfgWUc5Qjc4O4tfeB+W9ztNkv5kGStiQeTT3LZw0m4QjwGg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@express-rate-limit/prettier/-/prettier-1.1.0.tgz", + "integrity": "sha512-K/TUJI7P95QNiaY+fYkdBBIpyYKFKU1LiHsKZirAJKYyMLwSXMZvEVlCTChpQw6f+fOzDgib55rGB1yn5K8EhQ==", "dev": true }, "node_modules/@express-rate-limit/tsconfig": { From 9e6a84b045e7b29ef561421911fe2610e1e47b7a Mon Sep 17 00:00:00 2001 From: Vedant K Date: Sun, 27 Aug 2023 15:43:22 +0530 Subject: [PATCH 09/38] feat: run examples as an external test --- .github/workflows/ci.yaml | 22 +- examples/.npmrc | 6 + examples/combined-fetch.js | 37 - examples/draft-7-fetch.example.ts | 48 + ...thub-fetch.mjs => github-fetch.example.ts} | 3 + examples/package-lock.json | 1411 +++++++++++++---- examples/package.json | 24 +- examples/readme.md | 5 + examples/scripts/run-examples.ts | 37 + 9 files changed, 1269 insertions(+), 324 deletions(-) create mode 100644 examples/.npmrc delete mode 100644 examples/combined-fetch.js create mode 100644 examples/draft-7-fetch.example.ts rename examples/{github-fetch.mjs => github-fetch.example.ts} (81%) create mode 100644 examples/readme.md create mode 100755 examples/scripts/run-examples.ts diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e48f2e5..e85e8bb 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -42,9 +42,29 @@ jobs: run: | npm ci npm run test:lib + test-examples: + name: Test (Examples) + strategy: + matrix: + node-version: [lts/*] + os: [ubuntu-latest] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout the repository + uses: actions/checkout@v3 + - name: Use Node ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Run tests + run: | + npm ci + cd examples/ + npm ci + npm test publish: name: Publish - needs: [lint, test-library] + needs: [lint, test-library, test-examples] if: startsWith(github.ref, 'refs/tags/v') runs-on: ubuntu-latest steps: diff --git a/examples/.npmrc b/examples/.npmrc new file mode 100644 index 0000000..318a75f --- /dev/null +++ b/examples/.npmrc @@ -0,0 +1,6 @@ +# .npmrc +# Configuration for npm and pnpm + +# Uses the exact version instead of any within-patch-range version of an +# installed package +save-exact=true diff --git a/examples/combined-fetch.js b/examples/combined-fetch.js deleted file mode 100644 index b7f6c39..0000000 --- a/examples/combined-fetch.js +++ /dev/null @@ -1,37 +0,0 @@ -// This example has a server and client together - normally they'd be in separate JS files, -// likely on separate devices. - -// server -import { default as express } from 'express' -import { rateLimit } from 'express-rate-limit' -const app = express() - -app.use( - rateLimit({ - max: 5, - windowMs: 60 * 1000, // 1 minute - legacyHeader: false, // X-RateLimit-* - standardHeaders: 'draft-7', // combined RateLimit header - }), -) - -app.get('/', (req, res) => res.send('check headers')) - -const { port, server } = await new Promise((resolve) => { - const server = app.listen(0, () => - resolve({ port: server.address().port, server }), - ) -}) - -// client -import { parseRateLimit } from 'ratelimit-header-parser' - -const response = await fetch(`http://localhost:${port}`) - -console.log('RateLimit header:', response.headers.get('RateLimit')) -// > RateLimit header: limit=5, remaining=4, reset=60 -console.log('parsed ratelimit:', parseRateLimit(response)) -// > parsed ratelimit: { limit: 5, used: 1, remaining: 4, reset: 2023-08-25T04:41:31.546Z } - -// cleanup -server.close() diff --git a/examples/draft-7-fetch.example.ts b/examples/draft-7-fetch.example.ts new file mode 100644 index 0000000..c17d42c --- /dev/null +++ b/examples/draft-7-fetch.example.ts @@ -0,0 +1,48 @@ +// /examples/draft-7-fetch.example.ts +// Use `fetch`, and parse the `RateLimit` header from the IETF spec's 7th draft. + +// Note that example has a server and client together - normally they'd be in +// separate files, likely on separate devices. + +// --- +// `server.ts` +// ---- + +import { default as express } from 'express' +import { rateLimit } from 'express-rate-limit' + +// Create a rate-limited server. +const app = express() +app.use( + rateLimit({ + max: 5, + windowMs: 60 * 1000, // 1 minute windows. + legacyHeader: false, // Disable the `X-RateLimit-*` headers. + standardHeaders: 'draft-7', // Use the combined `RateLimit` header. + }), +) + +// Register routes, and start the server. +app.get('/', (req, res) => res.send('Hallo there!')) +const { port, server } = await new Promise((resolve) => { + const server = app.listen(0, () => + resolve({ port: server.address().port, server }), + ) +}) + +// --- +// `client.ts` +// --- + +import { parseRateLimit } from 'ratelimit-header-parser' + +// Fetch a response from the server. +const response = await fetch(`http://localhost:${port}`) + +console.log('`RateLimit` header content:', response.headers.get('RateLimit')) +// > `RateLimit` header content: limit=5, remaining=4, reset=60 +console.log('parsed rate limit info:', parseRateLimit(response)) +// > parsed rate limit info: { limit: 5, used: 1, remaining: 4, reset: 2023-08-25T04:41:31.546Z } + +// Cleanup the server. +server.close() diff --git a/examples/github-fetch.mjs b/examples/github-fetch.example.ts similarity index 81% rename from examples/github-fetch.mjs rename to examples/github-fetch.example.ts index af79310..f7087e6 100644 --- a/examples/github-fetch.mjs +++ b/examples/github-fetch.example.ts @@ -1,3 +1,6 @@ +// /examples/github-fetch.example.ts +// Uses `fetch` to hit the Github API. + import { parseRateLimit } from 'ratelimit-header-parser' const response = await fetch( diff --git a/examples/package-lock.json b/examples/package-lock.json index 2002201..6d54448 100644 --- a/examples/package-lock.json +++ b/examples/package-lock.json @@ -1,42 +1,50 @@ { "name": "ratelimit-header-parser-examples", - "version": "1.0.0", + "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ratelimit-header-parser-examples", - "version": "1.0.0", - "license": "MIT", + "version": "0.0.0", "dependencies": { - "express": "^4.18.2", - "express-rate-limit": "^6.9.0", - "ratelimit-header-parser": "*" + "express": "4.18.2", + "express-rate-limit": "6.9.0", + "ratelimit-header-parser": "../" }, "devDependencies": { - "ts-node": "^10.9.1", - "typescript": "^5.2.2" + "chalk": "5.3.0", + "execa": "8.0.1", + "globby": "13.2.2", + "tasuku": "2.0.1", + "tsx": "3.12.7", + "typescript": "5.2.2" } }, "..": { "version": "0.1.0", "license": "MIT", "devDependencies": { - "@express-rate-limit/prettier": "^1.0.0", - "@express-rate-limit/tsconfig": "^1.0.0", - "@jest/globals": "^29.6.3", - "@jest/types": "^29.6.3", - "@types/node": "^20.5.1", - "del-cli": "^5.0.0", - "dts-bundle-generator": "^8.0.1", - "esbuild": "^0.19.2", - "husky": "^8.0.3", - "jest": "^29.6.3", - "lint-staged": "^14.0.1", - "npm-run-all": "^4.1.5", - "ts-jest": "^29.1.1", - "typescript": "^5.1.6", - "xo": "^0.56.0" + "@express-rate-limit/prettier": "1.1.0", + "@express-rate-limit/tsconfig": "1.0.0", + "@jest/globals": "29.6.3", + "@jest/types": "29.6.3", + "@types/node": "20.5.1", + "del-cli": "5.0.0", + "dts-bundle-generator": "8.0.1", + "esbuild": "0.19.2", + "husky": "8.0.3", + "jest": "29.6.3", + "lint-staged": "14.0.1", + "npm-run-all": "4.1.5", + "pkgroll": "1.11.0", + "ts-jest": "29.1.1", + "tsx": "3.12.7", + "typescript": "5.1.6", + "xo": "0.56.0" + }, + "engines": { + "node": ">= 16" } }, "../node_modules/@aashutoshrathi/word-wrap": { @@ -9289,252 +9297,658 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "node_modules/@esbuild-kit/cjs-loader": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@esbuild-kit/cjs-loader/-/cjs-loader-2.4.2.tgz", + "integrity": "sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" + "@esbuild-kit/core-utils": "^3.0.0", + "get-tsconfig": "^4.4.0" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "node_modules/@esbuild-kit/core-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.1.0.tgz", + "integrity": "sha512-Uuk8RpCg/7fdHSceR1M6XbSZFSuMrxcePFuGgyvsBn+u339dk5OeL4jv2EojwTN2st/unJGsVm4qHWjWNmJ/tw==", "dev": true, - "engines": { - "node": ">=6.0.0" + "dependencies": { + "esbuild": "~0.17.6", + "source-map-support": "^0.5.21" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "node_modules/@esbuild-kit/esm-loader": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.5.5.tgz", + "integrity": "sha512-Qwfvj/qoPbClxCRNuac1Du01r9gvNOT+pMYtJDapfB1eoGN1YlJ1BixLyL9WVENRx5RXgNLdfYdx/CuswlGhMw==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@esbuild-kit/core-utils": "^3.0.0", + "get-tsconfig": "^4.4.0" } }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.5.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.6.tgz", - "integrity": "sha512-Gi5wRGPbbyOTX+4Y2iULQ27oUPrefaB0PxGQJnfyWN3kvEDGM3mIB5M/gQLmitZf7A9FmLeaqxD3L1CXpm3VKQ==", + "node_modules/@esbuild/android-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", + "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", + "cpu": [ + "arm" + ], "dev": true, - "peer": true - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 0.6" + "node": ">=12" } }, - "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "node_modules/@esbuild/android-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", + "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", + "cpu": [ + "arm64" + ], "dev": true, - "bin": { - "acorn": "bin/acorn" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=0.4.0" + "node": ">=12" } }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "node_modules/@esbuild/android-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", + "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=0.4.0" + "node": ">=12" } }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "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.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", + "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">=12" } }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "node_modules/@esbuild/darwin-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", + "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">= 0.8" + "node": ">=12" } }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", + "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" } }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", + "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">= 0.6" + "node": ">=12" } }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "node_modules/@esbuild/linux-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", + "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.6" + "node": ">=12" } }, - "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "node_modules/@esbuild/linux-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", + "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.6" + "node": ">=12" } }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" + "node_modules/@esbuild/linux-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", + "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "node_modules/@esbuild/linux-loong64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", + "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.8" + "node": ">=12" } }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", + "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">=12" } }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", + "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", + "cpu": [ + "ppc64" + ], "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=0.3.1" + "node": ">=12" } }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "node_modules/@esbuild/linux-riscv64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", + "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.8" + "node": ">=12" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/etag": { + "node_modules/@esbuild/linux-s390x": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", + "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", + "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", + "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", + "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", + "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", + "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", + "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", + "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/yoga-layout": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@types/yoga-layout/-/yoga-layout-1.9.2.tgz", + "integrity": "sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "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.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/esbuild": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", + "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.17.19", + "@esbuild/android-arm64": "0.17.19", + "@esbuild/android-x64": "0.17.19", + "@esbuild/darwin-arm64": "0.17.19", + "@esbuild/darwin-x64": "0.17.19", + "@esbuild/freebsd-arm64": "0.17.19", + "@esbuild/freebsd-x64": "0.17.19", + "@esbuild/linux-arm": "0.17.19", + "@esbuild/linux-arm64": "0.17.19", + "@esbuild/linux-ia32": "0.17.19", + "@esbuild/linux-loong64": "0.17.19", + "@esbuild/linux-mips64el": "0.17.19", + "@esbuild/linux-ppc64": "0.17.19", + "@esbuild/linux-riscv64": "0.17.19", + "@esbuild/linux-s390x": "0.17.19", + "@esbuild/linux-x64": "0.17.19", + "@esbuild/netbsd-x64": "0.17.19", + "@esbuild/openbsd-x64": "0.17.19", + "@esbuild/sunos-x64": "0.17.19", + "@esbuild/win32-arm64": "0.17.19", + "@esbuild/win32-ia32": "0.17.19", + "@esbuild/win32-x64": "0.17.19" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", @@ -9542,6 +9956,29 @@ "node": ">= 0.6" } }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, "node_modules/express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", @@ -9594,6 +10031,43 @@ "express": "^4 || ^5" } }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/finalhandler": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", @@ -9627,6 +10101,20 @@ "node": ">= 0.6" } }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -9646,6 +10134,61 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.0.tgz", + "integrity": "sha512-pmjiZ7xtB8URYm74PlGJozDNyhvsVLUcpBa8DZBG3bWHwaHa9bPiRpiSfovw+fjhwONSCWKRyk+JQHEGZmMrzw==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "dev": true, + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -9694,6 +10237,15 @@ "node": ">= 0.8" } }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -9705,23 +10257,74 @@ "node": ">=0.10.0" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, "engines": { - "node": ">= 0.10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "node_modules/media-typer": { @@ -9737,6 +10340,21 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -9745,6 +10363,19 @@ "node": ">= 0.6" } }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -9775,6 +10406,18 @@ "node": ">= 0.6" } }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -9788,6 +10431,33 @@ "node": ">= 0.6" } }, + "node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/object-inspect": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", @@ -9807,6 +10477,21 @@ "node": ">= 0.8" } }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -9815,11 +10500,41 @@ "node": ">= 0.8" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -9846,6 +10561,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -9872,6 +10607,48 @@ "node": ">= 0.8" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -9943,6 +10720,27 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -9956,6 +10754,49 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -9964,6 +10805,42 @@ "node": ">= 0.8" } }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tasuku": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/tasuku/-/tasuku-2.0.1.tgz", + "integrity": "sha512-BXWDEJzpC1mUiOz5Csba85LS93o9a5pGKRTArLiXJZ2HGF/mXHIl+4SBF706Yxqg+GlJDQurvLxds8tC7EwyRA==", + "dev": true, + "dependencies": { + "yoga-layout-prebuilt": "1.10.0" + }, + "funding": { + "url": "https://github.com/privatenumber/tasuku?sponsor=1" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -9972,47 +10849,21 @@ "node": ">=0.6" } }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "node_modules/tsx": { + "version": "3.12.7", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-3.12.7.tgz", + "integrity": "sha512-C2Ip+jPmqKd1GWVQDvz/Eyc6QJbGfE7NrR3fx5BpEHMZsEHoIxHL1j+lKdGobr8ovEyqeNkPLSKp6SCSOt7gmw==", "dev": true, "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" + "@esbuild-kit/cjs-loader": "^2.4.2", + "@esbuild-kit/core-utils": "^3.0.0", + "@esbuild-kit/esm-loader": "^2.5.5" }, "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" + "tsx": "dist/cli.js" }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } + "optionalDependencies": { + "fsevents": "~2.3.2" } }, "node_modules/type-is": { @@ -10056,12 +10907,6 @@ "node": ">= 0.4.0" } }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -10070,13 +10915,31 @@ "node": ">= 0.8" } }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, "engines": { - "node": ">=6" + "node": ">= 8" + } + }, + "node_modules/yoga-layout-prebuilt": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/yoga-layout-prebuilt/-/yoga-layout-prebuilt-1.10.0.tgz", + "integrity": "sha512-YnOmtSbv4MTf7RGJMK0FvZ+KD8OEe/J5BNnR0GHhD8J/XcG/Qvxgszm0Un6FTHWW4uHlTgP0IztiXQnGyIR45g==", + "dev": true, + "dependencies": { + "@types/yoga-layout": "1.9.2" + }, + "engines": { + "node": ">=8" } } } diff --git a/examples/package.json b/examples/package.json index 304bc81..cd219ad 100644 --- a/examples/package.json +++ b/examples/package.json @@ -1,23 +1,23 @@ { "name": "ratelimit-header-parser-examples", - "version": "1.0.0", - "description": "", + "version": "0.0.0", + "description": "Examples to illustrate the usage of the `ratelimit-header-parser` library.", "private": true, - "main": "index.js", "type": "module", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "tsx scripts/run-examples.ts" }, "dependencies": { - "express": "^4.18.2", - "express-rate-limit": "^6.10.0", - "ratelimit-header-parser": "*" + "express": "4.18.2", + "express-rate-limit": "6.9.0", + "ratelimit-header-parser": "../" }, - "keywords": [], - "author": "", - "license": "MIT", "devDependencies": { - "ts-node": "^10.9.1", - "typescript": "^5.2.2" + "chalk": "5.3.0", + "execa": "8.0.1", + "globby": "13.2.2", + "tasuku": "2.0.1", + "tsx": "3.12.7", + "typescript": "5.2.2" } } diff --git a/examples/readme.md b/examples/readme.md new file mode 100644 index 0000000..ef7990c --- /dev/null +++ b/examples/readme.md @@ -0,0 +1,5 @@ +## Examples + +This folder contains examples of different `RateLimit` headers the library can +parse. To run a certain example, use `npx tsx .example.ts`. To run all the +examples in this folder, run `npm test`. diff --git a/examples/scripts/run-examples.ts b/examples/scripts/run-examples.ts new file mode 100755 index 0000000..9cab612 --- /dev/null +++ b/examples/scripts/run-examples.ts @@ -0,0 +1,37 @@ +// /examples/run-examples.ts +// Runs all the examples in the current folder. + +import process from 'node:process' +import { $ } from 'execa' +import { globby } from 'globby' +import chalk from 'chalk' +import task from 'tasuku' + +console.log(chalk.bold.inverse.green(' examples ')) +console.log() + +// Collect all the examples. +const files = await globby('*.example.ts') +const tasks = [] +for (const file of files) { + tasks.push( + task(file, async ({ setStatus, setOutput, setError }) => { + try { + // Run the example file with `tsx`. + const { stdout } = await $`npx tsx ${file}` + + setStatus('passed') + setOutput(stdout.replace(/\n/, '\n ')) // Align the output correctly. + } catch (error) { + // Display the error. + setStatus('failed') + setError(error) + } + }), + ) +} + +// Run all the examples, concurrently. +const results = await Promise.all(tasks) +// If any example hasn't run succesfully, fail the test. +if (results.find((result) => result.state !== 'success')) process.exit(1) From 3aab4dc2f87bb659356028cfd9998f5f993e0350 Mon Sep 17 00:00:00 2001 From: Vedant K Date: Mon, 28 Aug 2023 23:07:17 +0530 Subject: [PATCH 10/38] refc: extract more functions out, add a lot more comments --- package-lock.json | 19 +++ package.json | 3 +- source/ratelimit-header-parser.ts | 235 +++++++++++++++++------------- source/types.ts | 2 +- source/utilities.ts | 26 +++- test/parser-test.ts | 23 +-- 6 files changed, 192 insertions(+), 116 deletions(-) diff --git a/package-lock.json b/package-lock.json index dfd5e53..c50e8e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@jest/globals": "29.6.3", "@jest/types": "29.6.3", "@types/node": "20.5.1", + "cross-env": "7.0.3", "del-cli": "5.0.0", "dts-bundle-generator": "8.0.1", "esbuild": "0.19.2", @@ -3915,6 +3916,24 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", diff --git a/package.json b/package.json index 5732bb3..89ae192 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "format:code": "xo --fix", "format:rest": "prettier --ignore-unknown --write .", "format": "run-s format:*", - "test:lib": "jest --config config/jest.json", + "test:lib": "cross-env NODE_NO_WARNINGS=1 NODE_OPTIONS=--experimental-vm-modules jest --config config/jest.json", "test": "run-s lint test:lib", "pre-commit": "lint-staged", "prepare": "run-s compile && husky install config/husky" @@ -62,6 +62,7 @@ "@jest/globals": "29.6.3", "@jest/types": "29.6.3", "@types/node": "20.5.1", + "cross-env": "7.0.3", "del-cli": "5.0.0", "dts-bundle-generator": "8.0.1", "esbuild": "0.19.2", diff --git a/source/ratelimit-header-parser.ts b/source/ratelimit-header-parser.ts index a16ab49..336445e 100644 --- a/source/ratelimit-header-parser.ts +++ b/source/ratelimit-header-parser.ts @@ -1,5 +1,5 @@ // /source/ratelimit-header-parser.ts -// The parser and associated functions +// The parser and associated functions. import type { ResponseObject, @@ -7,7 +7,7 @@ import type { RateLimitInfo, ParserOptions, } from './types' -import { secondsToDate, toInt } from './utilities.js' +import { secondsToDate, toInt, getHeader } from './utilities.js' /** * Parses the passed response/headers object and returns rate limit information. @@ -41,94 +41,89 @@ export function parseRateLimit( } /** - * The internal parser function. + * The header parser function. */ -function parseHeaders( +const parseHeaders = ( headers: HeadersObject, options: Partial, -): RateLimitInfo | undefined { - const combined = getHeader(headers, 'ratelimit') - if (combined) return parseCombinedRateLimitHeader(combined) - - let prefix - if (getHeader(headers, 'ratelimit-remaining')) { - prefix = 'ratelimit-' - } else if (getHeader(headers, 'x-ratelimit-remaining')) { - prefix = 'x-ratelimit-' - } else if (getHeader(headers, 'x-rate-limit-remaining')) { - // Twitter - https://developer.twitter.com/en/docs/twitter-api/rate-limits#headers-and-codes - prefix = 'x-rate-limit-' - } else { - // Todo: handle other vendor-specific headers - see - // https://github.com/ietf-wg-httpapi/ratelimit-headers/issues/25 - // https://stackoverflow.com/questions/16022624/examples-of-http-api-rate-limiting-http-response-headers - // https://github.com/mre/rate-limits/blob/master/src/variants.rs - // etc. - return - } +): RateLimitInfo | undefined => { + // If the header is a combined header, parse it according to the 7th draft of + // the IETF spec. + const draft7Header = getHeader(headers, 'ratelimit') + if (draft7Header) return parseDraft7Header(draft7Header) - const limit = toInt(getHeader(headers, `${prefix}limit`)) - // Used - https://github.com/reddit-archive/reddit/wiki/API#rules - // used - https://docs.github.com/en/rest/overview/resources-in-the-rest-api?apiVersion=2022-11-28#rate-limit-headers - // observed - https://docs.gitlab.com/ee/administration/settings/user_and_ip_rate_limits.html#response-headers - // note that || is valid here because used should always be at least 1, and || handles NaN correctly, whereas ?? doesn't - const used = - toInt(getHeader(headers, `${prefix}used`)) || - toInt(getHeader(headers, `${prefix}observed`)) - const remaining = toInt(getHeader(headers, `${prefix}remaining`)) + // Find the prefix for the headers, e.g., `X-RateLimit-`, `RateLimit-`, etc. + const prefix = findPrefix(headers) + if (!prefix) return - let reset: Date | undefined - const resetRaw = getHeader(headers, `${prefix}reset`) - const resetType = options?.reset - switch (resetType) { - case 'date': { - reset = parseResetDate(resetRaw ?? '') - break - } - - case 'unix': { - reset = parseResetUnix(resetRaw ?? '') - break - } - - case 'seconds': { - reset = parseResetSeconds(resetRaw ?? '') - break - } + const limit = toInt(getHeader(headers, `${prefix}limit`)) - case 'milliseconds': { - reset = parseResetMilliseconds(resetRaw ?? '') - break - } + // Note that `||` is valid here because used should always be at least 1, and + // `||` handles NaN correctly, whereas `??` doesn't. + const used = toInt( + getHeader(headers, `${prefix}used`) || // eslint-disable-line @typescript-eslint/prefer-nullish-coalescing + getHeader(headers, `${prefix}observed`), + ) + const remaining = toInt(getHeader(headers, `${prefix}remaining`)) - default: { - if (resetRaw) reset = parseResetAuto(resetRaw) - else { - // Fallback to retry-after - const retryAfter = getHeader(headers, 'retry-after') - if (retryAfter) { - reset = parseResetUnix(retryAfter) - } - } - } - } + // Try parsing the reset header passed in the response. + let reset = parseResetHeader(getHeader(headers, `${prefix}reset`), options) + // If the reset header is not set, fallback to the retry-after header. + const retryAfter = getHeader(headers, 'retry-after') + if (!reset && retryAfter) reset = parseResetUnix(retryAfter) return { - limit: Number.isNaN(limit) ? used + remaining : limit, // Reddit omits - used: Number.isNaN(used) ? limit - remaining : used, // Most omit + limit: Number.isNaN(limit) ? used + remaining : limit, // Reddit omits this header. + used: Number.isNaN(used) ? limit - remaining : used, // Most APIs omit this header. remaining, reset, } } +/** + * Finds the prefix for the rate limit headers, e.g., `X-RateLimit-`, + * `RateLimit-`, etc. If none are found, it returns undefined. + * + * @param headers {HeadersObject} - The headers to search in. + * + * @returns {string | undefined} - The prefix, if any is found. + */ +const findPrefix = (headers: HeadersObject): string | undefined => { + // The draft-6 and unofficial rate limit headers. + if (getHeader(headers, 'ratelimit-remaining')) return 'ratelimit-' + if (getHeader(headers, 'x-ratelimit-remaining')) return 'x-ratelimit-' + + // Twitter - https://developer.twitter.com/en/docs/twitter-api/rate-limits#headers-and-codes + if (getHeader(headers, 'x-rate-limit-remaining')) return 'x-rate-limit-' + + // TODO: handle other vendor-specific headers - see + // https://github.com/ietf-wg-httpapi/ratelimit-headers/issues/25 + // https://stackoverflow.com/questions/16022624/examples-of-http-api-rate-limiting-http-response-headers + // https://github.com/mre/rate-limits/blob/master/src/variants.rs + // etc. + return undefined +} + +/** + * The regexps used to parse the `RateLimit` header. + */ const reLimit = /limit\s*=\s*(\d+)/i const reRemaining = /remaining\s*=\s*(\d+)/i const reReset = /reset\s*=\s*(\d+)/i -export function parseCombinedRateLimitHeader(header: string): RateLimitInfo { + +/** + * Parses a `RateLimit` header in accordance with the IETF spec's draft 7. + * + * @param header {string} - The contents of the `RateLimit` header. + * + * @returns {RateLimitInfo} - The normalised rate limit info. + */ +export const parseDraft7Header = (header: string): RateLimitInfo => { const limit = toInt(reLimit.exec(header)?.[1]) const remaining = toInt(reRemaining.exec(header)?.[1]) const resetSeconds = toInt(reReset.exec(header)?.[1]) const reset = secondsToDate(resetSeconds) + return { limit, used: limit - remaining, @@ -137,52 +132,86 @@ export function parseCombinedRateLimitHeader(header: string): RateLimitInfo { } } -function getHeader(headers: HeadersObject, name: string): string | undefined { - if ('get' in headers && typeof headers.get === 'function') { - return headers.get(name) ?? undefined // Returns null if missing, but everything else is undefined for missing values - } +/** + * Parses the `RateLimit-Reset` header's contents and returns a proper `Date`. + * + * @param header {string} - The header's contents. + */ +const parseResetHeader = ( + passedHeader: string | undefined, + options: Partial, +): Date | undefined => { + const header = passedHeader ?? '' + + let reset: Date | undefined + switch (options?.reset) { + case 'date': { + reset = parseResetDate(header) + break + } + + case 'unix': { + reset = parseResetUnix(header) + break + } + + case 'seconds': { + reset = parseResetSeconds(header) + break + } + + case 'milliseconds': { + reset = parseResetMilliseconds(header) + break + } - if (name in headers && typeof (headers as any)[name] === 'string') { - return (headers as any)[name] as string + default: { + reset = header ? parseResetAuto(header) : undefined + } } - return undefined + return reset } -function parseResetDate(resetRaw: string): Date { - // Todo: take the server's date into account, calculate an offset, then apply that to the current date - return new Date(resetRaw) -} +/** + * Parses the `Date` in the `RateLimit-Reset` header. + */ +const parseResetDate = (header: string): Date => + // TODO: take the server's date into account, calculate an offset, then apply that to the current date + new Date(header) -function parseResetUnix(resetRaw: string | number): Date { - const resetNumber = toInt(resetRaw) - return new Date(resetNumber * 1000) -} +/** + * Parses a unix epoch timestamp pased in the `RateLimit-Reset` header. + */ +const parseResetUnix = (header: string | number): Date => + new Date(toInt(header) * 1000) -function parseResetSeconds(resetRaw: string | number): Date { - const resetNumber = toInt(resetRaw) - return secondsToDate(resetNumber) -} +/** + * Converts the delta seconds in the `RateLimit-Reset` header to a proper date. + */ +const parseResetSeconds = (header: string | number): Date => + secondsToDate(toInt(header)) -function parseResetMilliseconds(resetRaw: string | number): Date { - const resetNumber = toInt(resetRaw) - return secondsToDate(resetNumber / 1000) -} +/** + * Converts the delta milliseconds in the `RateLimit-Reset` header to a proper date. + */ +const parseResetMilliseconds = (header: string | number): Date => + secondsToDate(toInt(header) / 1000) -const reLetters = /[a-z]/i -function parseResetAuto(resetRaw: string): Date { - // If it has any letters, assume it's a date string - if (reLetters.test(resetRaw)) { - return parseResetDate(resetRaw) - } +/** + * Find out what type of time is passed in the `RateLimit-Reset` header, and + * parse it into a `Date`. + */ +const parseResetAuto = (header: string): Date => { + // If it has any letters, assume it's a date string. + if (/[a-z]/i.test(header)) return parseResetDate(header) - const resetNumber = toInt(resetRaw) - // Looks like a unix timestamp - if (resetNumber && resetNumber > 1_000_000_000) { + const resetNumber = toInt(header) + // Looks like a unix timestamp, parse it as such. + if (resetNumber && resetNumber > 1_000_000_000) // Sometime in 2001 return parseResetUnix(resetNumber) - } - // Could be seconds or milliseconds (or something else!), defaulting to seconds + // Could be seconds or milliseconds (or something else!), defaulting to seconds. return parseResetSeconds(resetNumber) } diff --git a/source/types.ts b/source/types.ts index f37caba..aa4a8b8 100644 --- a/source/types.ts +++ b/source/types.ts @@ -1,5 +1,5 @@ // /source/types.ts -// All the types used by this package +// All the types used by this package. import type { ServerResponse, diff --git a/source/utilities.ts b/source/utilities.ts index c76403b..e41e004 100644 --- a/source/utilities.ts +++ b/source/utilities.ts @@ -1,5 +1,7 @@ // /source/utilities.ts -// The utility functions for the library +// The utility functions for the library. + +import type { HeadersObject } from './types.js' /** * Adds the given number of seconds to the current time and returns a `Date`. @@ -26,3 +28,25 @@ export const toInt = (input: string | number | undefined): number => { if (typeof input === 'number') return input return Number.parseInt(input ?? '', 10) } + +/** + * Returns a header (or undefined if it's not present) from the passed + * node/fetch-style header object. + * + * @param headers {HeadersObject} - The headers in the response. + * @param name {string} - The name of the header to return. + * + * @returns {string | undefined} - The contents of the header. + */ +export const getHeader = ( + headers: HeadersObject, + name: string, +): string | undefined => { + if ('get' in headers && typeof headers.get === 'function') + return headers.get(name) ?? undefined // Returns null if missing, but everything else is undefined for missing values + + if (name in headers && typeof (headers as any)[name] === 'string') + return (headers as any)[name] as string + + return undefined +} diff --git a/test/parser-test.ts b/test/parser-test.ts index a8aa89c..ec51e73 100644 --- a/test/parser-test.ts +++ b/test/parser-test.ts @@ -1,6 +1,9 @@ +// /test/parser-test.ts +// Tests for the public API. + import { describe, it, expect } from '@jest/globals' import { - parseCombinedRateLimitHeader, + parseDraft7Header, parseRateLimit, } from '../source/ratelimit-header-parser.js' @@ -45,15 +48,15 @@ describe('parseRateLimitHeaders', () => { // Todo: test parseResetAuto with various formats -describe('parseCombinedRateLimitHeader', () => { +describe('parseDraft7Header', () => { it('should parse a combined header', () => { - expect( - parseCombinedRateLimitHeader('limit=100, remaining=25, reset=5'), - ).toMatchObject({ - limit: 100, - remaining: 25, - used: 75, - reset: expect.any(Date), // Todo: mock the clock, then match to a specific date - }) + expect(parseDraft7Header('limit=100, remaining=25, reset=5')).toMatchObject( + { + limit: 100, + remaining: 25, + used: 75, + reset: expect.any(Date), // Todo: mock the clock, then match to a specific date + }, + ) }) }) From c2747bc11c56d10cac5dcf9508d58f199976b5d0 Mon Sep 17 00:00:00 2001 From: Vedant K Date: Mon, 18 Sep 2023 21:15:19 +0530 Subject: [PATCH 11/38] chore: move jest config to proper location --- config/jest.json => jest.config.json | 1 - package.json | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) rename config/jest.json => jest.config.json (93%) diff --git a/config/jest.json b/jest.config.json similarity index 93% rename from config/jest.json rename to jest.config.json index eeed9e0..a769899 100644 --- a/config/jest.json +++ b/jest.config.json @@ -1,5 +1,4 @@ { - "rootDir": "../", "preset": "ts-jest/presets/default-esm", "collectCoverage": true, "collectCoverageFrom": ["source/**/*.ts"], diff --git a/package.json b/package.json index 89ae192..546aff2 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "format:code": "xo --fix", "format:rest": "prettier --ignore-unknown --write .", "format": "run-s format:*", - "test:lib": "cross-env NODE_NO_WARNINGS=1 NODE_OPTIONS=--experimental-vm-modules jest --config config/jest.json", + "test:lib": "jest", "test": "run-s lint test:lib", "pre-commit": "lint-staged", "prepare": "run-s compile && husky install config/husky" From 94e272f9971520812275c9a7658e416a865df606 Mon Sep 17 00:00:00 2001 From: Vedant K Date: Mon, 18 Sep 2023 21:21:05 +0530 Subject: [PATCH 12/38] chore: rename `source/ratelimit-header-parser.ts` -> `source/parser.ts` --- source/index.ts | 2 +- source/{ratelimit-header-parser.ts => parser.ts} | 0 test/parser-test.ts | 5 +---- 3 files changed, 2 insertions(+), 5 deletions(-) rename source/{ratelimit-header-parser.ts => parser.ts} (100%) diff --git a/source/index.ts b/source/index.ts index fab3f14..b61b18e 100644 --- a/source/index.ts +++ b/source/index.ts @@ -5,4 +5,4 @@ export * from './types.js' // Export the public API as named exports too. -export { parseRateLimit } from './ratelimit-header-parser.js' +export { parseRateLimit } from './parser.js' diff --git a/source/ratelimit-header-parser.ts b/source/parser.ts similarity index 100% rename from source/ratelimit-header-parser.ts rename to source/parser.ts diff --git a/test/parser-test.ts b/test/parser-test.ts index ec51e73..ad0bbe0 100644 --- a/test/parser-test.ts +++ b/test/parser-test.ts @@ -2,10 +2,7 @@ // Tests for the public API. import { describe, it, expect } from '@jest/globals' -import { - parseDraft7Header, - parseRateLimit, -} from '../source/ratelimit-header-parser.js' +import { parseDraft7Header, parseRateLimit } from '../source/parser.js' const itif = (condition: boolean) => (condition ? it : it.skip) From 89650075696d13d29ef085ea419175e99441d17b Mon Sep 17 00:00:00 2001 From: Vedant K Date: Mon, 18 Sep 2023 22:04:07 +0530 Subject: [PATCH 13/38] feat: support more headers --- source/parser.ts | 69 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 15 deletions(-) diff --git a/source/parser.ts b/source/parser.ts index 336445e..881c18e 100644 --- a/source/parser.ts +++ b/source/parser.ts @@ -9,6 +9,20 @@ import type { } from './types' import { secondsToDate, toInt, getHeader } from './utilities.js' +/** + * The following links might be referred to in the below lines of code: + * + * [1]: https://github.com/ietf-wg-httpapi/ratelimit-headers/issues/25 + * [2]: https://docs.gitlab.com/ee/administration/settings/user_and_ip_rate_limits.html#response-headers + * [3]: https://techdocs.akamai.com/adaptive-media-delivery/reference/rate-limiting + * [4]: https://developer.twitter.com/en/docs/twitter-api/rate-limits#headers-and-codes + * [5]: https://developers.linear.app/docs/graphql/working-with-the-graphql-api/rate-limiting#api-request-limits + * [6]: https://apidocs.imgur.com/ + * [7]: https://stackoverflow.com/questions/16022624/examples-of-http-api-rate-limiting-http-response-headers + * [8]: https://github.com/mre/rate-limits/blob/master/src/headers/variants.rs + * + */ + /** * Parses the passed response/headers object and returns rate limit information. * @@ -17,10 +31,10 @@ import { secondsToDate, toInt, getHeader } from './utilities.js' * * @returns {RateLimitInfo | undefined} - The rate limit information parsed from the headers. */ -export function parseRateLimit( +export const parseRateLimit = ( input: ResponseObject | HeadersObject, passedOptions?: Partial, -): RateLimitInfo | undefined { +): RateLimitInfo | undefined => { // Default to no configuration. const options = passedOptions ?? {} @@ -52,22 +66,38 @@ const parseHeaders = ( const draft7Header = getHeader(headers, 'ratelimit') if (draft7Header) return parseDraft7Header(draft7Header) - // Find the prefix for the headers, e.g., `X-RateLimit-`, `RateLimit-`, etc. + // Find the type of headers sent by the server, e.g., `X-RateLimit-`, `RateLimit-`, etc. const prefix = findPrefix(headers) if (!prefix) return - const limit = toInt(getHeader(headers, `${prefix}limit`)) + // Note that `||` is valid in the following lines because used should always + // be at least 1, and `||` handles NaN correctly, whereas `??` doesn't. + /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ + + const limit = toInt( + getHeader(headers, `${prefix}limit`) || + getHeader(headers, `${prefix}dailylimit`) || // Yelp does this [1]. + getHeader(headers, `${prefix}max`), // Amazon does this [1]. + ) - // Note that `||` is valid here because used should always be at least 1, and - // `||` handles NaN correctly, whereas `??` doesn't. const used = toInt( - getHeader(headers, `${prefix}used`) || // eslint-disable-line @typescript-eslint/prefer-nullish-coalescing - getHeader(headers, `${prefix}observed`), + getHeader(headers, `${prefix}used`) || + getHeader(headers, `${prefix}observed`), // GitLab does this [2]. ) + const remaining = toInt(getHeader(headers, `${prefix}remaining`)) // Try parsing the reset header passed in the response. - let reset = parseResetHeader(getHeader(headers, `${prefix}reset`), options) + let reset = parseResetHeader( + getHeader(headers, `${prefix}reset`) || + getHeader(headers, `${prefix}resettime`) || // Yelp does this [1]. + getHeader(headers, `${prefix}resetson`) || // Amazon does this [1]. + getHeader(headers, `${prefix}next`), // Akamai does this [3]. + options, + ) + + /* eslint-enable @typescript-eslint/prefer-nullish-coalescing */ + // If the reset header is not set, fallback to the retry-after header. const retryAfter = getHeader(headers, 'retry-after') if (!reset && retryAfter) reset = parseResetUnix(retryAfter) @@ -93,14 +123,23 @@ const findPrefix = (headers: HeadersObject): string | undefined => { if (getHeader(headers, 'ratelimit-remaining')) return 'ratelimit-' if (getHeader(headers, 'x-ratelimit-remaining')) return 'x-ratelimit-' - // Twitter - https://developer.twitter.com/en/docs/twitter-api/rate-limits#headers-and-codes + // Twitter does this [4]. if (getHeader(headers, 'x-rate-limit-remaining')) return 'x-rate-limit-' - // TODO: handle other vendor-specific headers - see - // https://github.com/ietf-wg-httpapi/ratelimit-headers/issues/25 - // https://stackoverflow.com/questions/16022624/examples-of-http-api-rate-limiting-http-response-headers - // https://github.com/mre/rate-limits/blob/master/src/variants.rs - // etc. + // Linear does this [5]. + if (getHeader(headers, 'x-ratelimit-requests-remaining')) + return 'x-ratelimit-requests-' + if (getHeader(headers, 'x-ratelimit-complexity-remaining')) + return 'x-ratelimit-complexity-' + + // Imgur does this [6]. + if (getHeader(headers, 'x-ratelimit-userremaining')) return 'x-ratelimit-user' + if (getHeader(headers, 'x-ratelimit-clientremaining')) + return 'x-ratelimit-client' + if (getHeader(headers, 'x-post-rate-limit-remaining')) + return 'x-post-rate-limit-' + + // TODO: handle more headers, see links [7] and [8]. return undefined } From e5eb05c464bf3da1ba06303e8d24f8487eefade7 Mon Sep 17 00:00:00 2001 From: Vedant K Date: Mon, 18 Sep 2023 22:10:15 +0530 Subject: [PATCH 14/38] chore!: rename `parseRateLimit` to `getRateLimit` --- examples/draft-7-fetch.example.ts | 4 ++-- examples/github-fetch.example.ts | 4 ++-- readme.md | 6 +++--- source/index.ts | 2 +- source/parser.ts | 2 +- test/parser-test.ts | 8 ++++---- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/examples/draft-7-fetch.example.ts b/examples/draft-7-fetch.example.ts index c17d42c..d6f7f44 100644 --- a/examples/draft-7-fetch.example.ts +++ b/examples/draft-7-fetch.example.ts @@ -34,14 +34,14 @@ const { port, server } = await new Promise((resolve) => { // `client.ts` // --- -import { parseRateLimit } from 'ratelimit-header-parser' +import { getRateLimit } from 'ratelimit-header-parser' // Fetch a response from the server. const response = await fetch(`http://localhost:${port}`) console.log('`RateLimit` header content:', response.headers.get('RateLimit')) // > `RateLimit` header content: limit=5, remaining=4, reset=60 -console.log('parsed rate limit info:', parseRateLimit(response)) +console.log('parsed rate limit info:', getRateLimit(response)) // > parsed rate limit info: { limit: 5, used: 1, remaining: 4, reset: 2023-08-25T04:41:31.546Z } // Cleanup the server. diff --git a/examples/github-fetch.example.ts b/examples/github-fetch.example.ts index f7087e6..0740ba7 100644 --- a/examples/github-fetch.example.ts +++ b/examples/github-fetch.example.ts @@ -1,11 +1,11 @@ // /examples/github-fetch.example.ts // Uses `fetch` to hit the Github API. -import { parseRateLimit } from 'ratelimit-header-parser' +import { getRateLimit } from 'ratelimit-header-parser' const response = await fetch( 'https://api.github.com/repos/express-rate-limit/express-rate-limit/contributors?anon=1', ) -console.log('github ratelimit:', parseRateLimit(response)) +console.log('github ratelimit:', getRateLimit(response)) // > github ratelimit: { limit: 60, used: 1, remaining: 59, reset: 2023-08-25T04:16:48.000Z } diff --git a/readme.md b/readme.md index 2c34127..9736f53 100644 --- a/readme.md +++ b/readme.md @@ -19,13 +19,13 @@ the uncombined `RateLimit-*` format of earlier drafts, traditional ## Usage ```ts -import { parseRateLimit } from 'ratelimit-header-parser' +import { getRateLimit } from 'ratelimit-header-parser' const response = await fetch( 'https://api.github.com/repos/express-rate-limit/express-rate-limit/contributors?anon=1', ) -console.log('github ratelimit:', parseRateLimit(response)) +console.log('github ratelimit:', getRateLimit(response)) // > github ratelimit: { limit: 60, used: 1, remaining: 59, reset: 2023-08-25T04:16:48.000Z } ``` @@ -33,7 +33,7 @@ For more examples, take a look at the [`examples/`](examples/) folder. ## API -### `parseRateLimit(responseOrHeaders, [options]) => object | undefined` +### `getRateLimit(responseOrHeaders, [options]) => object | undefined` Scans the input for ratelimit headers in a variety of formats and returns the result in a consistent format, or undefined if it fails to find any rate-limit diff --git a/source/index.ts b/source/index.ts index b61b18e..f25c456 100644 --- a/source/index.ts +++ b/source/index.ts @@ -5,4 +5,4 @@ export * from './types.js' // Export the public API as named exports too. -export { parseRateLimit } from './parser.js' +export { getRateLimit } from './parser.js' diff --git a/source/parser.ts b/source/parser.ts index 881c18e..fe44a3a 100644 --- a/source/parser.ts +++ b/source/parser.ts @@ -31,7 +31,7 @@ import { secondsToDate, toInt, getHeader } from './utilities.js' * * @returns {RateLimitInfo | undefined} - The rate limit information parsed from the headers. */ -export const parseRateLimit = ( +export const getRateLimit = ( input: ResponseObject | HeadersObject, passedOptions?: Partial, ): RateLimitInfo | undefined => { diff --git a/test/parser-test.ts b/test/parser-test.ts index ad0bbe0..961e8f1 100644 --- a/test/parser-test.ts +++ b/test/parser-test.ts @@ -2,11 +2,11 @@ // Tests for the public API. import { describe, it, expect } from '@jest/globals' -import { parseDraft7Header, parseRateLimit } from '../source/parser.js' +import { parseDraft7Header, getRateLimit } from '../source/parser.js' const itif = (condition: boolean) => (condition ? it : it.skip) -describe('parseRateLimitHeaders', () => { +describe('getRateLimit', () => { // Note: Headers doesn't exist in node 16 or older itif(typeof Headers !== 'undefined')( 'should handle X-RateLimit-* headers in a fetch Headers object', @@ -16,7 +16,7 @@ describe('parseRateLimitHeaders', () => { 'X-RateLimit-Remaining': '70', 'X-RateLimit-Reset': Math.floor(Date.now() / 1000).toString(), }) - expect(parseRateLimit(headers)).toMatchObject({ + expect(getRateLimit(headers)).toMatchObject({ limit: 100, remaining: 70, used: 30, @@ -31,7 +31,7 @@ describe('parseRateLimitHeaders', () => { 'ratelimit-remaining': '20', 'ratelimit-reset': new Date().toISOString(), } - expect(parseRateLimit(headers)).toMatchObject({ + expect(getRateLimit(headers)).toMatchObject({ limit: 60, remaining: 20, used: 40, From 199cc12cd22907327e9c0e242e9738807be29a8c Mon Sep 17 00:00:00 2001 From: Vedant K Date: Mon, 18 Sep 2023 22:16:48 +0530 Subject: [PATCH 15/38] docs: add installation section to readme --- readme.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index 9736f53..319a411 100644 --- a/readme.md +++ b/readme.md @@ -16,16 +16,59 @@ of the the uncombined `RateLimit-*` format of earlier drafts, traditional `X-RateLimit-*` headers, and a few other formats. +## Installation + +From the npm registry: + +```sh +# Using npm +> npm install ratelimit-header-parser +# Using yarn or pnpm +> yarn/pnpm add ratelimit-header-parser +``` + +From Github Releases: + +```sh +# Using npm +> npm install https://github.com/express-rate-limit/ratelimit-header-parser/releases/download/v{version}/ratelimit-header-parser.tgz +# Using yarn or pnpm +> yarn/pnpm add https://github.com/express-rate-limit/ratelimit-header-parser/releases/download/v{version}/ratelimit-header-parser.tgz +``` + +Replace `{version}` with the version of the package that you want to your, e.g.: +`1.0.0`. + ## Usage +### Importing + +This library is provided in ESM as well as CJS forms, and works with both +Javascript and Typescript projects. + +**This package requires you to use Node 16 or above.** + +Import it in a CommonJS project (`type: commonjs` or no `type` field in +`package.json`) as follows: + ```ts -import { getRateLimit } from 'ratelimit-header-parser' +const { rateLimit } = require('express-rate-limit') +``` -const response = await fetch( - 'https://api.github.com/repos/express-rate-limit/express-rate-limit/contributors?anon=1', -) +Import it in a ESM project (`type: module` in `package.json`) as follows: + +```ts +import { rateLimit } from 'express-rate-limit' +``` +### Examples + +```ts +import { getRateLimit } from 'ratelimit-header-parser' + +const response = await fetch(https://api.github.com/orgs/express-rate-limit) console.log('github ratelimit:', getRateLimit(response)) + // > github ratelimit: { limit: 60, used: 1, remaining: 59, reset: 2023-08-25T04:16:48.000Z } ``` From ecbcb44351333ea312c5607b3980f2587e58062f Mon Sep 17 00:00:00 2001 From: Vedant K Date: Mon, 18 Sep 2023 22:17:44 +0530 Subject: [PATCH 16/38] chore: fix permissions for git hook --- config/husky/pre-commit | 4 ++++ 1 file changed, 4 insertions(+) create mode 100755 config/husky/pre-commit diff --git a/config/husky/pre-commit b/config/husky/pre-commit new file mode 100755 index 0000000..d0612ad --- /dev/null +++ b/config/husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npm run pre-commit From 857480bcf5a7c214847971a5fafcb57c95d73107 Mon Sep 17 00:00:00 2001 From: Vedant K Date: Tue, 19 Sep 2023 11:48:17 +0530 Subject: [PATCH 17/38] feat: add support for `x-mws-quota` headers (amazon) --- source/parser.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/parser.ts b/source/parser.ts index fe44a3a..3d2b213 100644 --- a/source/parser.ts +++ b/source/parser.ts @@ -20,7 +20,6 @@ import { secondsToDate, toInt, getHeader } from './utilities.js' * [6]: https://apidocs.imgur.com/ * [7]: https://stackoverflow.com/questions/16022624/examples-of-http-api-rate-limiting-http-response-headers * [8]: https://github.com/mre/rate-limits/blob/master/src/headers/variants.rs - * */ /** @@ -139,6 +138,9 @@ const findPrefix = (headers: HeadersObject): string | undefined => { if (getHeader(headers, 'x-post-rate-limit-remaining')) return 'x-post-rate-limit-' + // Amazon does this [1]. + if (getHeader(headers, 'x-mws-quota-remaining')) return 'x-mws-quota-' + // TODO: handle more headers, see links [7] and [8]. return undefined } From 6c2b2d0d0b37b9666d190146ced68bfd04ad76cb Mon Sep 17 00:00:00 2001 From: Vedant K Date: Tue, 19 Sep 2023 18:22:47 +0530 Subject: [PATCH 18/38] docs: fix syntax error in readme example --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 319a411..e654833 100644 --- a/readme.md +++ b/readme.md @@ -66,7 +66,7 @@ import { rateLimit } from 'express-rate-limit' ```ts import { getRateLimit } from 'ratelimit-header-parser' -const response = await fetch(https://api.github.com/orgs/express-rate-limit) +const response = await fetch('https://api.github.com/orgs/express-rate-limit') console.log('github ratelimit:', getRateLimit(response)) // > github ratelimit: { limit: 60, used: 1, remaining: 59, reset: 2023-08-25T04:16:48.000Z } From e66d90486b4055de145f60d63cd92eef9fe670bf Mon Sep 17 00:00:00 2001 From: Vedant K Date: Tue, 19 Sep 2023 18:58:29 +0530 Subject: [PATCH 19/38] test: add more examples --- examples/node/axios.ts | 11 +++ .../draft-7-fetch.ts} | 2 +- examples/node/http-get.ts | 14 ++++ .../native-fetch.ts} | 11 ++- examples/package-lock.json | 73 +++++++++++++++++++ examples/package.json | 3 +- examples/{scripts => }/run-examples.ts | 2 +- 7 files changed, 107 insertions(+), 9 deletions(-) create mode 100644 examples/node/axios.ts rename examples/{draft-7-fetch.example.ts => node/draft-7-fetch.ts} (97%) create mode 100644 examples/node/http-get.ts rename examples/{github-fetch.example.ts => node/native-fetch.ts} (51%) rename examples/{scripts => }/run-examples.ts (93%) mode change 100755 => 100644 diff --git a/examples/node/axios.ts b/examples/node/axios.ts new file mode 100644 index 0000000..5b32f14 --- /dev/null +++ b/examples/node/axios.ts @@ -0,0 +1,11 @@ +// /examples/node/axios.ts +// Uses `axios` to hit the GitHub API. + +import axios from 'axios' +import { getRateLimit } from 'ratelimit-header-parser' + +// Make a GET request to the GitHub API. +const response = await axios('https://api.github.com/orgs/express-rate-limit') +console.log('github ratelimit:', getRateLimit(response)) + +// > github ratelimit: { limit: 60, used: 1, remaining: 59, reset: 2023-08-25T04:16:48.000Z } diff --git a/examples/draft-7-fetch.example.ts b/examples/node/draft-7-fetch.ts similarity index 97% rename from examples/draft-7-fetch.example.ts rename to examples/node/draft-7-fetch.ts index d6f7f44..89bd257 100644 --- a/examples/draft-7-fetch.example.ts +++ b/examples/node/draft-7-fetch.ts @@ -1,4 +1,4 @@ -// /examples/draft-7-fetch.example.ts +// /examples/node/draft-7-fetch.ts // Use `fetch`, and parse the `RateLimit` header from the IETF spec's 7th draft. // Note that example has a server and client together - normally they'd be in diff --git a/examples/node/http-get.ts b/examples/node/http-get.ts new file mode 100644 index 0000000..21ae027 --- /dev/null +++ b/examples/node/http-get.ts @@ -0,0 +1,14 @@ +// /examples/node/http-get.ts +// Uses `http.get` to hit the Imgur API. + +import https from 'node:https' +import { getRateLimit } from 'ratelimit-header-parser' + +// Make a GET request to the Imgur API. +https.get('https://api.imgur.com/post/v1/posts/t/aww', (response) => { + console.log('imgur ratelimit:', getRateLimit(response)) + + return response.resume() +}) + +// > imgur ratelimit: { limit: 500, used: 1, remaining: 499, reset: 2023-09-19T14:19:30.297Z } diff --git a/examples/github-fetch.example.ts b/examples/node/native-fetch.ts similarity index 51% rename from examples/github-fetch.example.ts rename to examples/node/native-fetch.ts index 0740ba7..1704d10 100644 --- a/examples/github-fetch.example.ts +++ b/examples/node/native-fetch.ts @@ -1,11 +1,10 @@ -// /examples/github-fetch.example.ts -// Uses `fetch` to hit the Github API. +// /examples/node/native-fetch.ts +// Uses `fetch` to hit the GitHub API. import { getRateLimit } from 'ratelimit-header-parser' -const response = await fetch( - 'https://api.github.com/repos/express-rate-limit/express-rate-limit/contributors?anon=1', -) - +// Make a GET request to the GitHub API. +const response = await fetch('https://api.github.com/orgs/express-rate-limit') console.log('github ratelimit:', getRateLimit(response)) + // > github ratelimit: { limit: 60, used: 1, remaining: 59, reset: 2023-08-25T04:16:48.000Z } diff --git a/examples/package-lock.json b/examples/package-lock.json index 6d54448..3baffbb 100644 --- a/examples/package-lock.json +++ b/examples/package-lock.json @@ -8,6 +8,7 @@ "name": "ratelimit-header-parser-examples", "version": "0.0.0", "dependencies": { + "axios": "1.5.0", "express": "4.18.2", "express-rate-limit": "6.9.0", "ratelimit-header-parser": "../" @@ -30,6 +31,7 @@ "@jest/globals": "29.6.3", "@jest/types": "29.6.3", "@types/node": "20.5.1", + "cross-env": "7.0.3", "del-cli": "5.0.0", "dts-bundle-generator": "8.0.1", "esbuild": "0.19.2", @@ -9737,6 +9739,21 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", + "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -9810,6 +9827,17 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -9864,6 +9892,14 @@ "ms": "2.0.0" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -10085,6 +10121,38 @@ "node": ">= 0.8" } }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -10547,6 +10615,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", diff --git a/examples/package.json b/examples/package.json index cd219ad..b77d834 100644 --- a/examples/package.json +++ b/examples/package.json @@ -5,9 +5,10 @@ "private": true, "type": "module", "scripts": { - "test": "tsx scripts/run-examples.ts" + "test": "tsx run-examples.ts" }, "dependencies": { + "axios": "1.5.0", "express": "4.18.2", "express-rate-limit": "6.9.0", "ratelimit-header-parser": "../" diff --git a/examples/scripts/run-examples.ts b/examples/run-examples.ts old mode 100755 new mode 100644 similarity index 93% rename from examples/scripts/run-examples.ts rename to examples/run-examples.ts index 9cab612..cdd9956 --- a/examples/scripts/run-examples.ts +++ b/examples/run-examples.ts @@ -11,7 +11,7 @@ console.log(chalk.bold.inverse.green(' examples ')) console.log() // Collect all the examples. -const files = await globby('*.example.ts') +const files = await globby('node/*.ts', 'deno/*.ts', 'browser/*.ts') const tasks = [] for (const file of files) { tasks.push( From a06fd713432db1aec3173350aa8adca990466d7f Mon Sep 17 00:00:00 2001 From: Vedant K Date: Wed, 20 Sep 2023 14:59:51 +0530 Subject: [PATCH 20/38] test: add `node-fetch` example --- examples/node/node-fetch.ts | 11 +++++ examples/package-lock.json | 85 +++++++++++++++++++++++++++++++++++++ examples/package.json | 1 + 3 files changed, 97 insertions(+) create mode 100644 examples/node/node-fetch.ts diff --git a/examples/node/node-fetch.ts b/examples/node/node-fetch.ts new file mode 100644 index 0000000..0486da2 --- /dev/null +++ b/examples/node/node-fetch.ts @@ -0,0 +1,11 @@ +// /examples/node/node-fetch.ts +// Uses `node-fetch` to hit the GitHub API. + +import fetch from 'node-fetch' +import { getRateLimit } from 'ratelimit-header-parser' + +// Make a GET request to the GitHub API. +const response = await fetch('https://api.github.com/orgs/express-rate-limit') +console.log('github ratelimit:', getRateLimit(response)) + +// > github ratelimit: { limit: 60, used: 1, remaining: 59, reset: 2023-08-25T04:16:48.000Z } diff --git a/examples/package-lock.json b/examples/package-lock.json index 3baffbb..d31af4a 100644 --- a/examples/package-lock.json +++ b/examples/package-lock.json @@ -11,6 +11,7 @@ "axios": "1.5.0", "express": "4.18.2", "express-rate-limit": "6.9.0", + "node-fetch": "3.3.2", "ratelimit-header-parser": "../" }, "devDependencies": { @@ -9884,6 +9885,14 @@ "node": ">= 8" } }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "engines": { + "node": ">= 12" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -10092,6 +10101,28 @@ "reusify": "^1.0.4" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -10153,6 +10184,17 @@ "node": ">= 6" } }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -10499,6 +10541,41 @@ "node": ">= 0.6" } }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/npm-run-path": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", @@ -10988,6 +11065,14 @@ "node": ">= 0.8" } }, + "node_modules/web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "engines": { + "node": ">= 8" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/examples/package.json b/examples/package.json index b77d834..a9da648 100644 --- a/examples/package.json +++ b/examples/package.json @@ -11,6 +11,7 @@ "axios": "1.5.0", "express": "4.18.2", "express-rate-limit": "6.9.0", + "node-fetch": "3.3.2", "ratelimit-header-parser": "../" }, "devDependencies": { From 915b5c9ac3af4e763a202d6e72491e315a8040a5 Mon Sep 17 00:00:00 2001 From: Vedant K Date: Wed, 20 Sep 2023 15:39:14 +0530 Subject: [PATCH 21/38] test: add `deno/fetch` example --- examples/deno/fetch.ts | 10 +++++++ examples/run-examples.ts | 57 ++++++++++++++++++++++++---------------- 2 files changed, 44 insertions(+), 23 deletions(-) create mode 100644 examples/deno/fetch.ts diff --git a/examples/deno/fetch.ts b/examples/deno/fetch.ts new file mode 100644 index 0000000..15a6f12 --- /dev/null +++ b/examples/deno/fetch.ts @@ -0,0 +1,10 @@ +// /examples/deno/fetch.ts +// Uses `fetch` to hit the Imgur API. + +import { getRateLimit } from '../../dist/index.mjs' + +// Make a GET request to the Imgur API. +const response = await fetch('https://api.imgur.com/post/v1/posts/t/aww') +console.log('imgur ratelimit:', getRateLimit(response)) + +// > imgur ratelimit: { limit: 500, used: 1, remaining: 499, reset: 2023-08-25T04:16:48.000Z } diff --git a/examples/run-examples.ts b/examples/run-examples.ts index cdd9956..5ef058a 100644 --- a/examples/run-examples.ts +++ b/examples/run-examples.ts @@ -10,28 +10,39 @@ import task from 'tasuku' console.log(chalk.bold.inverse.green(' examples ')) console.log() -// Collect all the examples. -const files = await globby('node/*.ts', 'deno/*.ts', 'browser/*.ts') -const tasks = [] -for (const file of files) { - tasks.push( - task(file, async ({ setStatus, setOutput, setError }) => { - try { - // Run the example file with `tsx`. - const { stdout } = await $`npx tsx ${file}` - - setStatus('passed') - setOutput(stdout.replace(/\n/, '\n ')) // Align the output correctly. - } catch (error) { - // Display the error. - setStatus('failed') - setError(error) - } - }), - ) +const execute = async (files: string[], args: string[]): Promise => { + const tasks = [] + for (const file of files) { + tasks.push( + task(file, async ({ setStatus, setOutput, setError }) => { + try { + // Run the example file. + const { stdout } = await $`${args} ${file}` + + setStatus('passed') + setOutput(stdout.replace(/\n/, '\n ')) // Align the output correctly. + } catch (error) { + // Display the error. + setStatus('failed') + setError(error) + } + }), + ) + } + + // Run all the examples, concurrently. + const results = await Promise.all(tasks) + + // If any example hasn't run succesfully, fail the test. + if (results.find((result) => result.state !== 'success')) return false + else return true } -// Run all the examples, concurrently. -const results = await Promise.all(tasks) -// If any example hasn't run succesfully, fail the test. -if (results.find((result) => result.state !== 'success')) process.exit(1) +const node = await execute(await globby('node/*.ts'), ['npx', 'tsx']) +const deno = await execute(await globby('deno/*.ts'), [ + 'deno', + 'run', + '--allow-net', +]) + +if (!node || !deno) process.exit(1) From 6c0390eeb296bdad3cb8bda461fbfec7028b9083 Mon Sep 17 00:00:00 2001 From: Vedant K Date: Wed, 20 Sep 2023 15:40:28 +0530 Subject: [PATCH 22/38] ci: setup deno in example testing step --- .github/workflows/ci.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e85e8bb..5ddbdfe 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -47,6 +47,7 @@ jobs: strategy: matrix: node-version: [lts/*] + deno-version: [v1.x] os: [ubuntu-latest] runs-on: ${{ matrix.os }} steps: @@ -56,6 +57,10 @@ jobs: uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} + - name: Use Deno ${{ matrix.deno-version }} + uses: denoland/setup-deno@v1 + with: + deno-version: ${{ matrix.deno-version }} - name: Run tests run: | npm ci From 90e16d8863ad8492eae4f46677e5869a6c9795f7 Mon Sep 17 00:00:00 2001 From: Vedant K Date: Thu, 21 Sep 2023 16:13:24 +0530 Subject: [PATCH 23/38] chore: fix jest config --- jest.config.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/jest.config.json b/jest.config.json index a769899..7993dc4 100644 --- a/jest.config.json +++ b/jest.config.json @@ -4,8 +4,16 @@ "collectCoverageFrom": ["source/**/*.ts"], "testTimeout": 30000, "testMatch": ["**/test/*-test.ts"], - "moduleFileExtensions": ["js", "jsx", "json", "ts", "tsx"], + "extensionsToTreatAsEsm": [".ts"], "moduleNameMapper": { "^(\\.{1,2}/.*)\\.js$": "$1" + }, + "transform": { + "^.+\\.[tj]sx?$": [ + "ts-jest", + { + "useESM": true + } + ] } } From b67fae75f851d301697340172bbed94a8ddaebb6 Mon Sep 17 00:00:00 2001 From: Vedant K Date: Thu, 21 Sep 2023 16:17:43 +0530 Subject: [PATCH 24/38] feat: add support for parsing multiple rate limit headers --- source/index.ts | 2 +- source/parser.ts | 79 +++++++++++++++++++++++++++++++++--------------- 2 files changed, 56 insertions(+), 25 deletions(-) diff --git a/source/index.ts b/source/index.ts index f25c456..3aedafb 100644 --- a/source/index.ts +++ b/source/index.ts @@ -5,4 +5,4 @@ export * from './types.js' // Export the public API as named exports too. -export { getRateLimit } from './parser.js' +export { getRateLimit, getRateLimits } from './parser.js' diff --git a/source/parser.ts b/source/parser.ts index 3d2b213..0935a83 100644 --- a/source/parser.ts +++ b/source/parser.ts @@ -23,7 +23,9 @@ import { secondsToDate, toInt, getHeader } from './utilities.js' */ /** - * Parses the passed response/headers object and returns rate limit information. + * Parses the passed response/headers object and returns rate limit information + * extracted from one of: `RateLimit-`, `X-RateLimit-`, etc. See the `findPrefixes` + * function for the order in which the parser searches for headers. * * @param input {ResponseObject | HeadersObject} - The node/fetch-style response/headers object. * @param passedOptions {Partial | undefined} - The configuration for the parser. @@ -34,6 +36,23 @@ export const getRateLimit = ( input: ResponseObject | HeadersObject, passedOptions?: Partial, ): RateLimitInfo | undefined => { + const rateLimits = getRateLimits(input, passedOptions) + return rateLimits.length === 0 ? undefined : rateLimits[0] +} + +/** + * Parses the passed response/headers object and returns rate limit information + * extracted from ALL rate limit headers the parser can find. + * + * @param input {ResponseObject | HeadersObject} - The node/fetch-style response/headers object. + * @param passedOptions {Partial | undefined} - The configuration for the parser. + * + * @returns {RateLimitInfo[]} - The rate limit information parsed from the headers. + */ +export const getRateLimits = ( + input: ResponseObject | HeadersObject, + passedOptions?: Partial, +): RateLimitInfo[] => { // Default to no configuration. const options = passedOptions ?? {} @@ -49,8 +68,24 @@ export const getRateLimit = ( headers = input.getHeaders() else headers = input as HeadersObject - // Parse the headers. - return parseHeaders(headers, options) + // If the header is a combined header, parse it according to the 7th draft of + // the IETF spec. + const draft7Header = getHeader(headers, 'ratelimit') + const draft7RateLimit = draft7Header + ? parseDraft7Header(draft7Header) + : undefined + + // Find the type of headers sent by the server, e.g., `X-RateLimit-`, `RateLimit-`, etc. + const prefixes = findPrefixes(headers) + if (prefixes.length === 0) return [] + + // Parse each of the rate limit headers found. + const rateLimits = [draft7RateLimit] + for (const prefix of prefixes) + rateLimits.push(parseHeaders(headers, options, prefix)) + + // Return all non-undefined rate limits. + return rateLimits.filter((info) => info !== undefined) as RateLimitInfo[] } /** @@ -59,16 +94,8 @@ export const getRateLimit = ( const parseHeaders = ( headers: HeadersObject, options: Partial, + prefix: string, ): RateLimitInfo | undefined => { - // If the header is a combined header, parse it according to the 7th draft of - // the IETF spec. - const draft7Header = getHeader(headers, 'ratelimit') - if (draft7Header) return parseDraft7Header(draft7Header) - - // Find the type of headers sent by the server, e.g., `X-RateLimit-`, `RateLimit-`, etc. - const prefix = findPrefix(headers) - if (!prefix) return - // Note that `||` is valid in the following lines because used should always // be at least 1, and `||` handles NaN correctly, whereas `??` doesn't. /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ @@ -115,34 +142,38 @@ const parseHeaders = ( * * @param headers {HeadersObject} - The headers to search in. * - * @returns {string | undefined} - The prefix, if any is found. + * @returns {string[]} - The prefix, if any is found. */ -const findPrefix = (headers: HeadersObject): string | undefined => { +const findPrefixes = (headers: HeadersObject): string[] => { + const prefixes = [] + // The draft-6 and unofficial rate limit headers. - if (getHeader(headers, 'ratelimit-remaining')) return 'ratelimit-' - if (getHeader(headers, 'x-ratelimit-remaining')) return 'x-ratelimit-' + if (getHeader(headers, 'ratelimit-remaining')) prefixes.push('ratelimit-') + if (getHeader(headers, 'x-ratelimit-remaining')) prefixes.push('x-ratelimit-') // Twitter does this [4]. - if (getHeader(headers, 'x-rate-limit-remaining')) return 'x-rate-limit-' + if (getHeader(headers, 'x-rate-limit-remaining')) + prefixes.push('x-rate-limit-') // Linear does this [5]. if (getHeader(headers, 'x-ratelimit-requests-remaining')) - return 'x-ratelimit-requests-' + prefixes.push('x-ratelimit-requests-') if (getHeader(headers, 'x-ratelimit-complexity-remaining')) - return 'x-ratelimit-complexity-' + prefixes.push('x-ratelimit-complexity-') // Imgur does this [6]. - if (getHeader(headers, 'x-ratelimit-userremaining')) return 'x-ratelimit-user' + if (getHeader(headers, 'x-ratelimit-userremaining')) + prefixes.push('x-ratelimit-user') if (getHeader(headers, 'x-ratelimit-clientremaining')) - return 'x-ratelimit-client' + prefixes.push('x-ratelimit-client') if (getHeader(headers, 'x-post-rate-limit-remaining')) - return 'x-post-rate-limit-' + prefixes.push('x-post-rate-limit-') // Amazon does this [1]. - if (getHeader(headers, 'x-mws-quota-remaining')) return 'x-mws-quota-' + if (getHeader(headers, 'x-mws-quota-remaining')) prefixes.push('x-mws-quota-') // TODO: handle more headers, see links [7] and [8]. - return undefined + return prefixes } /** From ce1cff4522145b7739df2a070cda5f5a7df65689 Mon Sep 17 00:00:00 2001 From: Vedant K Date: Thu, 21 Sep 2023 16:18:06 +0530 Subject: [PATCH 25/38] test: use `getRateLimits` in `node/http-get` example --- examples/node/http-get.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/node/http-get.ts b/examples/node/http-get.ts index 21ae027..399bfd3 100644 --- a/examples/node/http-get.ts +++ b/examples/node/http-get.ts @@ -2,13 +2,13 @@ // Uses `http.get` to hit the Imgur API. import https from 'node:https' -import { getRateLimit } from 'ratelimit-header-parser' +import { getRateLimits } from 'ratelimit-header-parser' // Make a GET request to the Imgur API. https.get('https://api.imgur.com/post/v1/posts/t/aww', (response) => { - console.log('imgur ratelimit:', getRateLimit(response)) - + console.log('imgur ratelimit:', getRateLimits(response)) return response.resume() }) -// > imgur ratelimit: { limit: 500, used: 1, remaining: 499, reset: 2023-09-19T14:19:30.297Z } +// > imgur ratelimit: { limit: 500, used: 1, remaining: 499, reset: 2023-09-19T14:19:30.297Z } +// > { limit: 12500, used: 1, remaining: 12499, reset: 2023-09-19T14:19:30.297Z } From 524e3516f9cfe219c0492d798b679744886f5d5f Mon Sep 17 00:00:00 2001 From: Vedant K Date: Thu, 21 Sep 2023 17:10:02 +0530 Subject: [PATCH 26/38] fix: convert all header names to lowercase if headers object is json --- source/parser.ts | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/source/parser.ts b/source/parser.ts index 0935a83..a14977d 100644 --- a/source/parser.ts +++ b/source/parser.ts @@ -62,11 +62,27 @@ export const getRateLimits = ( 'headers' in input && typeof input.headers === 'object' && !Array.isArray(input.headers) - ) + ) { + // The input is a fetch-style response object, the headers are a property on + // the object. headers = input.headers - else if ('getHeaders' in input && typeof input.getHeaders === 'function') + } else if ('getHeaders' in input && typeof input.getHeaders === 'function') { + // The input is a node-style response object, get the headers using the + // `getHeaders` function. headers = input.getHeaders() - else headers = input as HeadersObject + } else if ( + 'getSetCookie' in input && + typeof input.getSetCookie === 'function' + ) { + // The input is a node-style response object. + headers = input as HeadersObject + } else { + // The input is a JSON object that contains all the headers. Make sure all + // the header names are in lower case. + headers = Object.fromEntries( + Object.entries(input).map(([k, v]) => [k.toLowerCase(), v]), + ) as HeadersObject + } // If the header is a combined header, parse it according to the 7th draft of // the IETF spec. From 9122e1651b2996026273ffe5a57e13afbaa8198b Mon Sep 17 00:00:00 2001 From: Vedant K Date: Thu, 21 Sep 2023 17:37:53 +0530 Subject: [PATCH 27/38] test: increase coverage --- source/parser.ts | 2 +- test/parser-test.ts | 122 ++++++++++++++++++++++++++++++-------------- 2 files changed, 86 insertions(+), 38 deletions(-) diff --git a/source/parser.ts b/source/parser.ts index a14977d..d0c6903 100644 --- a/source/parser.ts +++ b/source/parser.ts @@ -67,7 +67,7 @@ export const getRateLimits = ( // the object. headers = input.headers } else if ('getHeaders' in input && typeof input.getHeaders === 'function') { - // The input is a node-style response object, get the headers using the + // The input is a node `ServerResponse` object, get the headers using the // `getHeaders` function. headers = input.getHeaders() } else if ( diff --git a/test/parser-test.ts b/test/parser-test.ts index 961e8f1..18a219e 100644 --- a/test/parser-test.ts +++ b/test/parser-test.ts @@ -2,58 +2,106 @@ // Tests for the public API. import { describe, it, expect } from '@jest/globals' -import { parseDraft7Header, getRateLimit } from '../source/parser.js' +import { + parseDraft7Header, + getRateLimit, + getRateLimits, +} from '../source/parser.js' const itif = (condition: boolean) => (condition ? it : it.skip) -describe('getRateLimit', () => { - // Note: Headers doesn't exist in node 16 or older +describe('input tests', () => { + const headers = { + 'X-RateLimit-Limit': '100', + 'X-RateLimit-Remaining': '70', + 'X-RateLimit-Reset': Date.now().toString(), + } + const info = { + limit: 100, + remaining: 70, + used: 30, + reset: expect.any(Date), + } + + // NOTE: the `Header` class doesn't exist in node 16 or older. itif(typeof Headers !== 'undefined')( - 'should handle X-RateLimit-* headers in a fetch Headers object', + 'should handle headers in a fetch-style headers object', () => { - const headers = new Headers({ - 'X-RateLimit-Limit': '100', - 'X-RateLimit-Remaining': '70', - 'X-RateLimit-Reset': Math.floor(Date.now() / 1000).toString(), - }) - expect(getRateLimit(headers)).toMatchObject({ - limit: 100, - remaining: 70, - used: 30, - reset: expect.any(Date), // Todo: mock the clock, then match to a specific date - }) + expect(getRateLimit(new Headers(headers))).toMatchObject(info) }, ) - it('should handle RateLimit-* headers in a node-style headers object', () => { + it('should handle headers in a node-style headers object', () => { + expect(getRateLimit(headers)).toMatchObject(info) + }) + + it('should handle headers in a node `ServerResponse` object', () => { + const response = new Response('Hallo!', { headers }) + + expect(getRateLimit(response)).toMatchObject(info) + }) +}) + +describe('api tests', () => { + it('should parse a header and return the parsed info', () => { const headers = { - 'ratelimit-limit': '60', - 'ratelimit-remaining': '20', - 'ratelimit-reset': new Date().toISOString(), + 'X-Rate-Limit-Limit': '500', + 'X-Rate-Limit-Remaining': '499', + 'X-Rate-Limit-Reset': Date.now().toString(), + } + const info = { + limit: 500, + remaining: 499, + used: 1, + reset: expect.any(Date), } - expect(getRateLimit(headers)).toMatchObject({ - limit: 60, - remaining: 20, - used: 40, - reset: expect.any(Date), // Todo: mock the clock, then match to a specific date - }) + + expect(getRateLimit(headers)).toMatchObject(info) }) - // Todo: test with other object types - // todo: test with various options -}) + it('should parse multiple headers and return all of them', () => { + const headers = { + 'X-RateLimit-ClientLimit': '1000', + 'X-RateLimit-ClientRemaining': '999', + 'X-RateLimit-ClientReset': Date.now().toString(), -// Todo: test parseResetAuto with various formats + 'X-RateLimit-UserLimit': '60', + 'X-RateLimit-UserRemaining': '42', + 'X-RateLimit-UserReset': Date.now().toString(), + } -describe('parseDraft7Header', () => { - it('should parse a combined header', () => { - expect(parseDraft7Header('limit=100, remaining=25, reset=5')).toMatchObject( + // NOTE: the order in which the parsed info is returns depends on the order + // of detection in `source/parsers.ts#findPrefixes`. + const infos = [ + { + limit: 60, + remaining: 42, + used: 18, + reset: expect.any(Date), + }, { - limit: 100, - remaining: 25, - used: 75, - reset: expect.any(Date), // Todo: mock the clock, then match to a specific date + limit: 1000, + remaining: 999, + used: 1, + reset: expect.any(Date), }, - ) + ] + + expect(getRateLimits(headers)).toMatchObject(infos) + }) + + it('should parse a combined header', () => { + const header = 'limit=100, remaining=25, reset=5' + const info = { + limit: 100, + remaining: 25, + used: 75, + reset: expect.any(Date), + } + + expect(parseDraft7Header(header)).toMatchObject(info) }) }) + +// TODO: test options +// TODO: test `parseResetAuto` with various formats From 30549d27eab1f6b497019f9c6e4513c55ef96ca5 Mon Sep 17 00:00:00 2001 From: Vedant K Date: Fri, 22 Sep 2023 15:54:56 +0530 Subject: [PATCH 28/38] test: use `ServerResponse` instead of `Response` --- test/parser-test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/parser-test.ts b/test/parser-test.ts index 18a219e..90c23b9 100644 --- a/test/parser-test.ts +++ b/test/parser-test.ts @@ -1,6 +1,7 @@ // /test/parser-test.ts // Tests for the public API. +import { ServerResponse } from 'node:http' import { describe, it, expect } from '@jest/globals' import { parseDraft7Header, @@ -36,7 +37,7 @@ describe('input tests', () => { }) it('should handle headers in a node `ServerResponse` object', () => { - const response = new Response('Hallo!', { headers }) + const response = new ServerResponse('Hallo!', { headers }) expect(getRateLimit(response)).toMatchObject(info) }) From 4d6c318a5f42af26fed9d1d4edd38f7de6a59919 Mon Sep 17 00:00:00 2001 From: Vedant K Date: Fri, 22 Sep 2023 16:29:23 +0530 Subject: [PATCH 29/38] test: `Response` only works with recent versions --- test/parser-test.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/test/parser-test.ts b/test/parser-test.ts index 90c23b9..172bad1 100644 --- a/test/parser-test.ts +++ b/test/parser-test.ts @@ -1,7 +1,6 @@ // /test/parser-test.ts // Tests for the public API. -import { ServerResponse } from 'node:http' import { describe, it, expect } from '@jest/globals' import { parseDraft7Header, @@ -24,7 +23,7 @@ describe('input tests', () => { reset: expect.any(Date), } - // NOTE: the `Header` class doesn't exist in node 16 or older. + // NOTE: the `Header` and `Response` classes don't exist in node 16 or older. itif(typeof Headers !== 'undefined')( 'should handle headers in a fetch-style headers object', () => { @@ -32,14 +31,17 @@ describe('input tests', () => { }, ) - it('should handle headers in a node-style headers object', () => { - expect(getRateLimit(headers)).toMatchObject(info) - }) + itif(typeof Response !== 'undefined')( + 'should handle headers in a node `ServerResponse` object', + () => { + const response = new Response('Hallo!', { headers }) - it('should handle headers in a node `ServerResponse` object', () => { - const response = new ServerResponse('Hallo!', { headers }) + expect(getRateLimit(response)).toMatchObject(info) + }, + ) - expect(getRateLimit(response)).toMatchObject(info) + it('should handle headers in a node-style headers object', () => { + expect(getRateLimit(headers)).toMatchObject(info) }) }) From fe96c7e8c4b156b7889daa96bdf0b78231f68333 Mon Sep 17 00:00:00 2001 From: Vedant K Date: Fri, 22 Sep 2023 17:06:42 +0530 Subject: [PATCH 30/38] test: add date tests --- jest.config.json | 8 -------- source/parser.ts | 4 ++-- test/parser-test.ts | 50 ++++++++++++++++++++++++++++++++++----------- 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/jest.config.json b/jest.config.json index 7993dc4..224feb9 100644 --- a/jest.config.json +++ b/jest.config.json @@ -7,13 +7,5 @@ "extensionsToTreatAsEsm": [".ts"], "moduleNameMapper": { "^(\\.{1,2}/.*)\\.js$": "$1" - }, - "transform": { - "^.+\\.[tj]sx?$": [ - "ts-jest", - { - "useESM": true - } - ] } } diff --git a/source/parser.ts b/source/parser.ts index d0c6903..08785db 100644 --- a/source/parser.ts +++ b/source/parser.ts @@ -225,7 +225,7 @@ export const parseDraft7Header = (header: string): RateLimitInfo => { * * @param header {string} - The header's contents. */ -const parseResetHeader = ( +export const parseResetHeader = ( passedHeader: string | undefined, options: Partial, ): Date | undefined => { @@ -290,7 +290,7 @@ const parseResetMilliseconds = (header: string | number): Date => * Find out what type of time is passed in the `RateLimit-Reset` header, and * parse it into a `Date`. */ -const parseResetAuto = (header: string): Date => { +export const parseResetAuto = (header: string): Date => { // If it has any letters, assume it's a date string. if (/[a-z]/i.test(header)) return parseResetDate(header) diff --git a/test/parser-test.ts b/test/parser-test.ts index 172bad1..966703f 100644 --- a/test/parser-test.ts +++ b/test/parser-test.ts @@ -1,14 +1,15 @@ // /test/parser-test.ts // Tests for the public API. -import { describe, it, expect } from '@jest/globals' +import { describe, test, expect } from '@jest/globals' import { parseDraft7Header, + parseResetAuto, getRateLimit, getRateLimits, } from '../source/parser.js' -const itif = (condition: boolean) => (condition ? it : it.skip) +const testIf = (condition: boolean) => (condition ? test : test.skip) describe('input tests', () => { const headers = { @@ -24,15 +25,15 @@ describe('input tests', () => { } // NOTE: the `Header` and `Response` classes don't exist in node 16 or older. - itif(typeof Headers !== 'undefined')( - 'should handle headers in a fetch-style headers object', + testIf(typeof Headers !== 'undefined')( + 'fetch-style headers object parsing', () => { expect(getRateLimit(new Headers(headers))).toMatchObject(info) }, ) - itif(typeof Response !== 'undefined')( - 'should handle headers in a node `ServerResponse` object', + testIf(typeof Response !== 'undefined')( + 'fetch-style response object parsing', () => { const response = new Response('Hallo!', { headers }) @@ -40,13 +41,13 @@ describe('input tests', () => { }, ) - it('should handle headers in a node-style headers object', () => { + test('node-style headers object parsing', () => { expect(getRateLimit(headers)).toMatchObject(info) }) }) describe('api tests', () => { - it('should parse a header and return the parsed info', () => { + test('json header object parsing', () => { const headers = { 'X-Rate-Limit-Limit': '500', 'X-Rate-Limit-Remaining': '499', @@ -62,7 +63,7 @@ describe('api tests', () => { expect(getRateLimit(headers)).toMatchObject(info) }) - it('should parse multiple headers and return all of them', () => { + test('multiple header parsing', () => { const headers = { 'X-RateLimit-ClientLimit': '1000', 'X-RateLimit-ClientRemaining': '999', @@ -93,7 +94,7 @@ describe('api tests', () => { expect(getRateLimits(headers)).toMatchObject(infos) }) - it('should parse a combined header', () => { + test('combined header (draft 7) parsing', () => { const header = 'limit=100, remaining=25, reset=5' const info = { limit: 100, @@ -106,5 +107,30 @@ describe('api tests', () => { }) }) -// TODO: test options -// TODO: test `parseResetAuto` with various formats +describe('date tests', () => { + const thatDay = new Date('2023-05-16T18:12:13.000Z') + + test('date string auto-detection', () => { + const dateString = 'Tuesday, May 16, 2023 11:42:13 PM GMT+05:30' + + const parsedDate = parseResetAuto(dateString) + expect(parsedDate.getTime()).toBe(thatDay.getTime()) + }) + + test('unix date auto-detection', () => { + const dateString = '1684260733' + + const parsedDate = parseResetAuto(dateString) + expect(parsedDate.getTime()).toBe(thatDay.getTime()) + }) + + test('delta seconds auto-detection', () => { + const now = new Date() + const then = new Date() + then.setSeconds(now.getSeconds() + 42) + const dateString = '42' + + const parsedDate = parseResetAuto(dateString) + expect(parsedDate.getTime()).toBe(then.getTime()) + }) +}) From 09e8a9c53b48ec92a372d3c1c70e99afd2e61dfb Mon Sep 17 00:00:00 2001 From: Vedant K Date: Mon, 9 Oct 2023 16:12:30 +0530 Subject: [PATCH 31/38] ci: enable provenance statement generation - https://github.com/express-rate-limit/express-rate-limit/discussions/406 --- .github/workflows/ci.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5ddbdfe..221c669 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -72,6 +72,9 @@ jobs: needs: [lint, test-library, test-examples] if: startsWith(github.ref, 'refs/tags/v') runs-on: ubuntu-latest + permissions: + contents: write + id-token: write steps: - name: Checkout the repository uses: actions/checkout@v3 @@ -82,13 +85,13 @@ jobs: - name: Install dependencies run: npm ci - name: Publish to npm - run: npm publish + run: npm publish --provenance env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Build package to upload to GitHub releases run: | npm pack - mv rratelimit-header-parser-*.tgz ratelimit-header-parser.tgz + mv ratelimit-header-parser-*.tgz ratelimit-header-parser.tgz - name: Create a Github release uses: softprops/action-gh-release@v1 with: From 0e2d81d491a1cfe79b8e600e76cd2669de6296af Mon Sep 17 00:00:00 2001 From: Nathan Friedly Date: Fri, 27 Oct 2023 06:56:32 -0400 Subject: [PATCH 32/38] sort multiple limits so that the one with the fewest remaining comes first --- source/parser.ts | 17 +++++++++++++---- test/parser-test.ts | 3 +-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/source/parser.ts b/source/parser.ts index 08785db..767e3a6 100644 --- a/source/parser.ts +++ b/source/parser.ts @@ -96,12 +96,21 @@ export const getRateLimits = ( if (prefixes.length === 0) return [] // Parse each of the rate limit headers found. - const rateLimits = [draft7RateLimit] + const potentialRateLimits = [draft7RateLimit] for (const prefix of prefixes) - rateLimits.push(parseHeaders(headers, options, prefix)) + potentialRateLimits.push(parseHeaders(headers, options, prefix)) - // Return all non-undefined rate limits. - return rateLimits.filter((info) => info !== undefined) as RateLimitInfo[] + // Filter out undefined rate limits. + const rateLimits = potentialRateLimits.filter( + (info) => info !== undefined, + ) as RateLimitInfo[] + + // Sort so that the limit with the lowest remaining value comes first + rateLimits.sort( + (a: RateLimitInfo, b: RateLimitInfo) => a.remaining - b.remaining, + ) + + return rateLimits } /** diff --git a/test/parser-test.ts b/test/parser-test.ts index 966703f..1ba7e1b 100644 --- a/test/parser-test.ts +++ b/test/parser-test.ts @@ -74,8 +74,7 @@ describe('api tests', () => { 'X-RateLimit-UserReset': Date.now().toString(), } - // NOTE: the order in which the parsed info is returns depends on the order - // of detection in `source/parsers.ts#findPrefixes`. + // NOTE: the result is sorted by remaining values with smallest first const infos = [ { limit: 60, From a7a353cdba8359d8c91f3b666c4fb735d5f42116 Mon Sep 17 00:00:00 2001 From: Nathan Friedly Date: Fri, 27 Oct 2023 14:39:15 -0400 Subject: [PATCH 33/38] split tests into separate files --- test/api-test.ts | 69 ++++++++++++++++++++++ test/date-test.ts | 30 ++++++++++ test/input-test.ts | 39 +++++++++++++ test/parser-test.ts | 135 -------------------------------------------- 4 files changed, 138 insertions(+), 135 deletions(-) create mode 100644 test/api-test.ts create mode 100644 test/date-test.ts create mode 100644 test/input-test.ts delete mode 100644 test/parser-test.ts diff --git a/test/api-test.ts b/test/api-test.ts new file mode 100644 index 0000000..1fdf319 --- /dev/null +++ b/test/api-test.ts @@ -0,0 +1,69 @@ +// /test/parser-test.ts +// Tests for the public API. + +import { describe, test, expect } from '@jest/globals' +import { + parseDraft7Header, + getRateLimit, + getRateLimits, +} from '../source/parser.js' + +describe('api tests', () => { + test('json header object parsing', () => { + const headers = { + 'X-Rate-Limit-Limit': '500', + 'X-Rate-Limit-Remaining': '499', + 'X-Rate-Limit-Reset': Date.now().toString(), + } + const info = { + limit: 500, + remaining: 499, + used: 1, + reset: expect.any(Date), + } + + expect(getRateLimit(headers)).toMatchObject(info) + }) + + test('multiple header parsing', () => { + const headers = { + 'X-RateLimit-ClientLimit': '1000', + 'X-RateLimit-ClientRemaining': '999', + 'X-RateLimit-ClientReset': Date.now().toString(), + + 'X-RateLimit-UserLimit': '60', + 'X-RateLimit-UserRemaining': '42', + 'X-RateLimit-UserReset': Date.now().toString(), + } + + // NOTE: the result is sorted by remaining values with smallest first + const infos = [ + { + limit: 60, + remaining: 42, + used: 18, + reset: expect.any(Date), + }, + { + limit: 1000, + remaining: 999, + used: 1, + reset: expect.any(Date), + }, + ] + + expect(getRateLimits(headers)).toMatchObject(infos) + }) + + test('combined header (draft 7) parsing', () => { + const header = 'limit=100, remaining=25, reset=5' + const info = { + limit: 100, + remaining: 25, + used: 75, + reset: expect.any(Date), + } + + expect(parseDraft7Header(header)).toMatchObject(info) + }) +}) diff --git a/test/date-test.ts b/test/date-test.ts new file mode 100644 index 0000000..6fb9f4b --- /dev/null +++ b/test/date-test.ts @@ -0,0 +1,30 @@ +import { describe, test, expect } from '@jest/globals' +import { parseResetAuto } from '../source/parser.js' + +describe('date tests', () => { + const thatDay = new Date('2023-05-16T18:12:13.000Z') + + test('date string auto-detection', () => { + const dateString = 'Tuesday, May 16, 2023 11:42:13 PM GMT+05:30' + + const parsedDate = parseResetAuto(dateString) + expect(parsedDate.getTime()).toBe(thatDay.getTime()) + }) + + test('unix date auto-detection', () => { + const dateString = '1684260733' + + const parsedDate = parseResetAuto(dateString) + expect(parsedDate.getTime()).toBe(thatDay.getTime()) + }) + + test('delta seconds auto-detection', () => { + const now = new Date() + const then = new Date() + then.setSeconds(now.getSeconds() + 42) + const dateString = '42' + + const parsedDate = parseResetAuto(dateString) + expect(parsedDate.getTime()).toBe(then.getTime()) + }) +}) diff --git a/test/input-test.ts b/test/input-test.ts new file mode 100644 index 0000000..a4ed0d1 --- /dev/null +++ b/test/input-test.ts @@ -0,0 +1,39 @@ +import { describe, test, expect } from '@jest/globals' +import { getRateLimit } from '../source/parser.js' + +const testIf = (condition: boolean) => (condition ? test : test.skip) + +describe('input tests', () => { + const headers = { + 'X-RateLimit-Limit': '100', + 'X-RateLimit-Remaining': '70', + 'X-RateLimit-Reset': Date.now().toString(), + } + const info = { + limit: 100, + remaining: 70, + used: 30, + reset: expect.any(Date), + } + + // NOTE: the `Header` and `Response` classes don't exist in node 16 or older. + testIf(typeof Headers !== 'undefined')( + 'fetch-style headers object parsing', + () => { + expect(getRateLimit(new Headers(headers))).toMatchObject(info) + }, + ) + + testIf(typeof Response !== 'undefined')( + 'fetch-style response object parsing', + () => { + const response = new Response('Hallo!', { headers }) + + expect(getRateLimit(response)).toMatchObject(info) + }, + ) + + test('node-style headers object parsing', () => { + expect(getRateLimit(headers)).toMatchObject(info) + }) +}) diff --git a/test/parser-test.ts b/test/parser-test.ts deleted file mode 100644 index 1ba7e1b..0000000 --- a/test/parser-test.ts +++ /dev/null @@ -1,135 +0,0 @@ -// /test/parser-test.ts -// Tests for the public API. - -import { describe, test, expect } from '@jest/globals' -import { - parseDraft7Header, - parseResetAuto, - getRateLimit, - getRateLimits, -} from '../source/parser.js' - -const testIf = (condition: boolean) => (condition ? test : test.skip) - -describe('input tests', () => { - const headers = { - 'X-RateLimit-Limit': '100', - 'X-RateLimit-Remaining': '70', - 'X-RateLimit-Reset': Date.now().toString(), - } - const info = { - limit: 100, - remaining: 70, - used: 30, - reset: expect.any(Date), - } - - // NOTE: the `Header` and `Response` classes don't exist in node 16 or older. - testIf(typeof Headers !== 'undefined')( - 'fetch-style headers object parsing', - () => { - expect(getRateLimit(new Headers(headers))).toMatchObject(info) - }, - ) - - testIf(typeof Response !== 'undefined')( - 'fetch-style response object parsing', - () => { - const response = new Response('Hallo!', { headers }) - - expect(getRateLimit(response)).toMatchObject(info) - }, - ) - - test('node-style headers object parsing', () => { - expect(getRateLimit(headers)).toMatchObject(info) - }) -}) - -describe('api tests', () => { - test('json header object parsing', () => { - const headers = { - 'X-Rate-Limit-Limit': '500', - 'X-Rate-Limit-Remaining': '499', - 'X-Rate-Limit-Reset': Date.now().toString(), - } - const info = { - limit: 500, - remaining: 499, - used: 1, - reset: expect.any(Date), - } - - expect(getRateLimit(headers)).toMatchObject(info) - }) - - test('multiple header parsing', () => { - const headers = { - 'X-RateLimit-ClientLimit': '1000', - 'X-RateLimit-ClientRemaining': '999', - 'X-RateLimit-ClientReset': Date.now().toString(), - - 'X-RateLimit-UserLimit': '60', - 'X-RateLimit-UserRemaining': '42', - 'X-RateLimit-UserReset': Date.now().toString(), - } - - // NOTE: the result is sorted by remaining values with smallest first - const infos = [ - { - limit: 60, - remaining: 42, - used: 18, - reset: expect.any(Date), - }, - { - limit: 1000, - remaining: 999, - used: 1, - reset: expect.any(Date), - }, - ] - - expect(getRateLimits(headers)).toMatchObject(infos) - }) - - test('combined header (draft 7) parsing', () => { - const header = 'limit=100, remaining=25, reset=5' - const info = { - limit: 100, - remaining: 25, - used: 75, - reset: expect.any(Date), - } - - expect(parseDraft7Header(header)).toMatchObject(info) - }) -}) - -describe('date tests', () => { - const thatDay = new Date('2023-05-16T18:12:13.000Z') - - test('date string auto-detection', () => { - const dateString = 'Tuesday, May 16, 2023 11:42:13 PM GMT+05:30' - - const parsedDate = parseResetAuto(dateString) - expect(parsedDate.getTime()).toBe(thatDay.getTime()) - }) - - test('unix date auto-detection', () => { - const dateString = '1684260733' - - const parsedDate = parseResetAuto(dateString) - expect(parsedDate.getTime()).toBe(thatDay.getTime()) - }) - - test('delta seconds auto-detection', () => { - const now = new Date() - const then = new Date() - then.setSeconds(now.getSeconds() + 42) - const dateString = '42' - - const parsedDate = parseResetAuto(dateString) - expect(parsedDate.getTime()).toBe(then.getTime()) - }) -}) From 0332c1f7ea6f828aeeaf703de54f50503815e9bf Mon Sep 17 00:00:00 2001 From: Nathan Friedly Date: Fri, 27 Oct 2023 14:45:36 -0400 Subject: [PATCH 34/38] fix flakey test by mocking clock --- test/date-test.ts | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/test/date-test.ts b/test/date-test.ts index 6fb9f4b..cf5c56a 100644 --- a/test/date-test.ts +++ b/test/date-test.ts @@ -1,4 +1,11 @@ -import { describe, test, expect } from '@jest/globals' +import { + describe, + test, + expect, + jest, + afterEach, + beforeEach, +} from '@jest/globals' import { parseResetAuto } from '../source/parser.js' describe('date tests', () => { @@ -18,13 +25,22 @@ describe('date tests', () => { expect(parsedDate.getTime()).toBe(thatDay.getTime()) }) - test('delta seconds auto-detection', () => { - const now = new Date() - const then = new Date() - then.setSeconds(now.getSeconds() + 42) - const dateString = '42' + describe('mocked clock', () => { + beforeEach(() => { + jest.useFakeTimers() + }) + afterEach(() => { + jest.useRealTimers() + }) - const parsedDate = parseResetAuto(dateString) - expect(parsedDate.getTime()).toBe(then.getTime()) + test('delta seconds auto-detection', () => { + const now = new Date() + const then = new Date() + then.setSeconds(now.getSeconds() + 42) + const dateString = '42' + + const parsedDate = parseResetAuto(dateString) + expect(parsedDate.getTime()).toBe(then.getTime()) + }) }) }) From 54670f073be920814ead9ab1f5e6ce66d0014104 Mon Sep 17 00:00:00 2001 From: Nathan Friedly Date: Fri, 27 Oct 2023 15:13:44 -0400 Subject: [PATCH 35/38] make remaining optional also make sorting fall back to limit, and document getRateLimits function in readme --- readme.md | 19 +++++++++++++++---- source/parser.ts | 39 +++++++++++++++++++++++++++++++++------ source/types.ts | 4 ++-- source/utilities.ts | 17 +++++++++++++++-- test/sort-test.ts | 15 +++++++++++++++ 5 files changed, 80 insertions(+), 14 deletions(-) create mode 100644 test/sort-test.ts diff --git a/readme.md b/readme.md index e654833..3bff4fe 100644 --- a/readme.md +++ b/readme.md @@ -80,14 +80,17 @@ For more examples, take a look at the [`examples/`](examples/) folder. Scans the input for ratelimit headers in a variety of formats and returns the result in a consistent format, or undefined if it fails to find any rate-limit -headers. Returns an object with the following fields, or `undefined` if it does -not find any rate-limit headers. +headers. If multiple ratelimits are found, it chooses the one with the lowest +remaining value. + +Returns an object with the following fields, or `undefined` if it does not find +any rate-limit headers. ```ts type RateLimitInfo = { limit: number - used: number - remaining: number + used: number | undefined + remaining: number | undefined reset: Date | undefined } ``` @@ -112,6 +115,14 @@ type Options = { } ``` +### `getRateLimits(responseOrHeaders, [options]) => object[]` + +For APIs that may return multiple rate limits (e.g. per client & per end-user), +this will parse all of them. + +Accepts the same inputs as `getRateLimit` and returns an array containing zero +or more of the same `RateLimitInfo` objects that `getRateLimit` returns. + ## Issues and Contributing If you encounter a bug or want to see something added/changed, please go ahead diff --git a/source/parser.ts b/source/parser.ts index 767e3a6..cf2c83b 100644 --- a/source/parser.ts +++ b/source/parser.ts @@ -7,7 +7,12 @@ import type { RateLimitInfo, ParserOptions, } from './types' -import { secondsToDate, toInt, getHeader } from './utilities.js' +import { + secondsToDate, + toInt, + getHeader, + toIntOrUndefined, +} from './utilities.js' /** * The following links might be referred to in the below lines of code: @@ -40,6 +45,30 @@ export const getRateLimit = ( return rateLimits.length === 0 ? undefined : rateLimits[0] } +/** + * Function to sort an array of RateLimitInfo[] by remaining, then by limit, whith lower values coming first, and undefined remaining values coming after defined ones + * @param a {RateLimitInfo} + * @param b {RateLimitInfo} + * @returns number + */ +export function remainingSortFn(a: RateLimitInfo, b: RateLimitInfo): number { + const aDefined = a.remaining !== undefined + const bDefined = b.remaining !== undefined + if (a.remaining === b.remaining) { + return a.limit - b.limit + } + + if (aDefined && !bDefined) { + return -1 + } + + if (!aDefined && bDefined) { + return 1 + } + + return a.remaining! - b.remaining! +} + /** * Parses the passed response/headers object and returns rate limit information * extracted from ALL rate limit headers the parser can find. @@ -106,9 +135,7 @@ export const getRateLimits = ( ) as RateLimitInfo[] // Sort so that the limit with the lowest remaining value comes first - rateLimits.sort( - (a: RateLimitInfo, b: RateLimitInfo) => a.remaining - b.remaining, - ) + rateLimits.sort(remainingSortFn) return rateLimits } @@ -217,13 +244,13 @@ const reReset = /reset\s*=\s*(\d+)/i */ export const parseDraft7Header = (header: string): RateLimitInfo => { const limit = toInt(reLimit.exec(header)?.[1]) - const remaining = toInt(reRemaining.exec(header)?.[1]) + const remaining = toIntOrUndefined(reRemaining.exec(header)?.[1]) // Optional per https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers-07#name-ratelimit const resetSeconds = toInt(reReset.exec(header)?.[1]) const reset = secondsToDate(resetSeconds) return { limit, - used: limit - remaining, + used: typeof remaining === 'number' ? limit - remaining : undefined, remaining, reset, } diff --git a/source/types.ts b/source/types.ts index aa4a8b8..918714e 100644 --- a/source/types.ts +++ b/source/types.ts @@ -31,12 +31,12 @@ export type RateLimitInfo = { /** * The number of requests already made to that endpoint. */ - used: number + used?: number /** * The number of requests that can be made before reaching the rate limit. */ - remaining: number + remaining?: number /** * The timestamp at which the window resets, and one's hit count is set to zero. diff --git a/source/utilities.ts b/source/utilities.ts index e41e004..f2c0bb0 100644 --- a/source/utilities.ts +++ b/source/utilities.ts @@ -21,14 +21,27 @@ export const secondsToDate = (seconds: number): Date => { * * @param input {string | number | undefined} - The input to convert to a number. * - * @return {number} - The parsed integer. - * @throws {Error} - Thrown if the string does not contain a valid number. + * @return {number} - The parsed integer. May be NaN for unparseable input. */ export const toInt = (input: string | number | undefined): number => { if (typeof input === 'number') return input return Number.parseInt(input ?? '', 10) } +/** + * Converts a string/number to a number or undefined. + * + * @param input {string | number | undefined} - The input to convert to a number. + * + * @return {number | undefined} - The parsed integer. + */ +export const toIntOrUndefined = ( + input: string | number | undefined, +): number | undefined => { + const number_ = toInt(input) + return Number.isNaN(number_) ? undefined : number_ +} + /** * Returns a header (or undefined if it's not present) from the passed * node/fetch-style header object. diff --git a/test/sort-test.ts b/test/sort-test.ts new file mode 100644 index 0000000..7370fb5 --- /dev/null +++ b/test/sort-test.ts @@ -0,0 +1,15 @@ +import { describe, test, expect } from '@jest/globals' +import { remainingSortFn } from '../source/parser.js' + +describe('remainingSortFn', () => { + test('Should short with lowest remaining values first and undefined values last', () => { + const unknown70 = { limit: 70 } + const unknown75 = { limit: 75 } + const five100 = { limit: 100, remaining: 5, used: 95 } + const five101 = { limit: 101, remaining: 5, used: 95 } + const ten99 = { limit: 99, remaining: 10, used: 190 } + const infos = [unknown75, five101, ten99, unknown70, five100] + infos.sort(remainingSortFn) + expect(infos).toMatchObject([five100, five101, ten99, unknown70, unknown75]) + }) +}) From d75430fb94ff54b0d5b844c7854172693fb5762c Mon Sep 17 00:00:00 2001 From: Vedant K Date: Tue, 31 Oct 2023 12:25:34 +0530 Subject: [PATCH 36/38] docs: fix heading formatting --- readme.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 3bff4fe..0dc6cdc 100644 --- a/readme.md +++ b/readme.md @@ -1,10 +1,11 @@ -#
RateLimit Header Parser
+

ratelimit-header-parser

[![tests](https://github.com/express-rate-limit/ratelimit-header-parser/actions/workflows/ci.yaml/badge.svg)](https://github.com/express-rate-limit/ratelimit-header-parser/actions/workflows/ci.yaml) [![npm version](https://img.shields.io/npm/v/ratelimit-header-parser.svg)](https://npmjs.org/package/ratelimit-header-parser 'View this project on NPM') [![npm downloads](https://img.shields.io/npm/dm/ratelimit-header-parser)](https://www.npmjs.com/package/ratelimit-header-parser) +[![license](https://img.shields.io/npm/l/ratelimit-header-parser)](license.md)
From a9df287b2ba5010ff9e5560f0260540bf4c0b8b4 Mon Sep 17 00:00:00 2001 From: Vedant K Date: Tue, 31 Oct 2023 12:49:21 +0530 Subject: [PATCH 37/38] test: add failing test for ms auto-parse --- test/api-test.ts | 5 +++-- test/date-test.ts | 16 ++++++++++++++-- test/input-test.ts | 3 +++ test/sort-test.ts | 8 ++++++-- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/test/api-test.ts b/test/api-test.ts index 1fdf319..7a0b675 100644 --- a/test/api-test.ts +++ b/test/api-test.ts @@ -1,4 +1,4 @@ -// /test/parser-test.ts +// /test/api-test.ts // Tests for the public API. import { describe, test, expect } from '@jest/globals' @@ -36,7 +36,7 @@ describe('api tests', () => { 'X-RateLimit-UserReset': Date.now().toString(), } - // NOTE: the result is sorted by remaining values with smallest first + // NOTE: the result is sorted by remaining values with smallest first. const infos = [ { limit: 60, @@ -55,6 +55,7 @@ describe('api tests', () => { expect(getRateLimits(headers)).toMatchObject(infos) }) + // NOTE: `parseDraft7Header` is not a part of the public API. test('combined header (draft 7) parsing', () => { const header = 'limit=100, remaining=25, reset=5' const info = { diff --git a/test/date-test.ts b/test/date-test.ts index cf5c56a..81550e6 100644 --- a/test/date-test.ts +++ b/test/date-test.ts @@ -1,10 +1,13 @@ +// /test/date-test.ts +// Tests the date parsing functions. + import { + jest, describe, test, expect, - jest, - afterEach, beforeEach, + afterEach, } from '@jest/globals' import { parseResetAuto } from '../source/parser.js' @@ -25,6 +28,15 @@ describe('date tests', () => { expect(parsedDate.getTime()).toBe(thatDay.getTime()) }) + // NOTE: This test doesn't work because `parseResetAuto` defaults to parsing + // the date as seconds instead of milliseconds. + test.failing('milliseconds date auto-detection', () => { + const dateString = '1684260733000' + + const parsedDate = parseResetAuto(dateString) + expect(parsedDate.getTime()).toBe(thatDay.getTime()) + }) + describe('mocked clock', () => { beforeEach(() => { jest.useFakeTimers() diff --git a/test/input-test.ts b/test/input-test.ts index a4ed0d1..e5a329d 100644 --- a/test/input-test.ts +++ b/test/input-test.ts @@ -1,3 +1,6 @@ +// /test/input-test.ts +// Tests the types of objects the library can parse. + import { describe, test, expect } from '@jest/globals' import { getRateLimit } from '../source/parser.js' diff --git a/test/sort-test.ts b/test/sort-test.ts index 7370fb5..b41088b 100644 --- a/test/sort-test.ts +++ b/test/sort-test.ts @@ -1,14 +1,18 @@ +// /test/sort-test.ts +// Tests the sorting function. + import { describe, test, expect } from '@jest/globals' import { remainingSortFn } from '../source/parser.js' -describe('remainingSortFn', () => { - test('Should short with lowest remaining values first and undefined values last', () => { +describe('sort tests', () => { + test('should sort with lowest remaining values first and undefined values last', () => { const unknown70 = { limit: 70 } const unknown75 = { limit: 75 } const five100 = { limit: 100, remaining: 5, used: 95 } const five101 = { limit: 101, remaining: 5, used: 95 } const ten99 = { limit: 99, remaining: 10, used: 190 } const infos = [unknown75, five101, ten99, unknown70, five100] + infos.sort(remainingSortFn) expect(infos).toMatchObject([five100, five101, ten99, unknown70, unknown75]) }) From ae3dae1d3053ea86997671eaf0f9f6598964471a Mon Sep 17 00:00:00 2001 From: Vedant K Date: Tue, 31 Oct 2023 12:50:42 +0530 Subject: [PATCH 38/38] docs: add test badge --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 0dc6cdc..9210e34 100644 --- a/readme.md +++ b/readme.md @@ -2,7 +2,7 @@
-[![tests](https://github.com/express-rate-limit/ratelimit-header-parser/actions/workflows/ci.yaml/badge.svg)](https://github.com/express-rate-limit/ratelimit-header-parser/actions/workflows/ci.yaml) +[![tests](https://img.shields.io/github/actions/workflow/status/express-rate-limit/ratelimit-header-parser/ci.yaml)](https://github.com/express-rate-limit/ratelimit-header-parser/actions/workflows/ci.yaml) [![npm version](https://img.shields.io/npm/v/ratelimit-header-parser.svg)](https://npmjs.org/package/ratelimit-header-parser 'View this project on NPM') [![npm downloads](https://img.shields.io/npm/dm/ratelimit-header-parser)](https://www.npmjs.com/package/ratelimit-header-parser) [![license](https://img.shields.io/npm/l/ratelimit-header-parser)](license.md)