From 950702fafd18d99b76a8e7aa51a347511788dd44 Mon Sep 17 00:00:00 2001 From: psyko-gh Date: Mon, 19 Aug 2024 22:33:24 +0200 Subject: [PATCH] feat(core): initial commit --- .dockerignore | 2 + .gitignore | 47 + Dockerfile | 12 + LICENSE | Bin 0 -> 2236 bytes README.md | 276 + package-lock.json | 5667 ++++++++++++++++++ package.json | 54 + schema/schema.json | 235 + src/api/httpApi.ts | 143 + src/api/overseerr/index.ts | 81 + src/api/overseerr/interfaces.ts | 184 + src/api/plex/index.ts | 99 + src/api/plex/interfaces.ts | 44 + src/env.ts | 17 + src/index.ts | 54 + src/lib/cron.ts | 56 + src/lib/rules/__tests__/movie.json | 4131 +++++++++++++ src/lib/rules/__tests__/rules.ts | 329 + src/lib/rules/factory.ts | 66 + src/lib/rules/index.ts | 48 + src/lib/rules/interfaces.ts | 35 + src/lib/rules/predicate/and.ts | 21 + src/lib/rules/predicate/boolean.ts | 23 + src/lib/rules/predicate/cast.ts | 36 + src/lib/rules/predicate/crew.ts | 45 + src/lib/rules/predicate/genres.ts | 24 + src/lib/rules/predicate/group.ts | 10 + src/lib/rules/predicate/index.ts | 62 + src/lib/rules/predicate/keywords.ts | 36 + src/lib/rules/predicate/not.ts | 20 + src/lib/rules/predicate/number.ts | 42 + src/lib/rules/predicate/or.ts | 20 + src/lib/rules/predicate/productionCompany.ts | 36 + src/lib/rules/predicate/released.ts | 32 + src/lib/rules/predicate/tag.ts | 24 + src/lib/rules/predicate/time.ts | 29 + src/lib/rules/predicate/watchproviders.ts | 37 + src/lib/ruleset.ts | 98 + src/lib/settings.ts | 152 + src/lib/utils.ts | 71 + src/log.ts | 21 + src/routes/index.ts | 94 + src/service/index.ts | 123 + tsconfig.json | 111 + 44 files changed, 12747 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 schema/schema.json create mode 100644 src/api/httpApi.ts create mode 100644 src/api/overseerr/index.ts create mode 100644 src/api/overseerr/interfaces.ts create mode 100644 src/api/plex/index.ts create mode 100644 src/api/plex/interfaces.ts create mode 100644 src/env.ts create mode 100644 src/index.ts create mode 100644 src/lib/cron.ts create mode 100644 src/lib/rules/__tests__/movie.json create mode 100644 src/lib/rules/__tests__/rules.ts create mode 100644 src/lib/rules/factory.ts create mode 100644 src/lib/rules/index.ts create mode 100644 src/lib/rules/interfaces.ts create mode 100644 src/lib/rules/predicate/and.ts create mode 100644 src/lib/rules/predicate/boolean.ts create mode 100644 src/lib/rules/predicate/cast.ts create mode 100644 src/lib/rules/predicate/crew.ts create mode 100644 src/lib/rules/predicate/genres.ts create mode 100644 src/lib/rules/predicate/group.ts create mode 100644 src/lib/rules/predicate/index.ts create mode 100644 src/lib/rules/predicate/keywords.ts create mode 100644 src/lib/rules/predicate/not.ts create mode 100644 src/lib/rules/predicate/number.ts create mode 100644 src/lib/rules/predicate/or.ts create mode 100644 src/lib/rules/predicate/productionCompany.ts create mode 100644 src/lib/rules/predicate/released.ts create mode 100644 src/lib/rules/predicate/tag.ts create mode 100644 src/lib/rules/predicate/time.ts create mode 100644 src/lib/rules/predicate/watchproviders.ts create mode 100644 src/lib/ruleset.ts create mode 100644 src/lib/settings.ts create mode 100644 src/lib/utils.ts create mode 100644 src/log.ts create mode 100644 src/routes/index.ts create mode 100644 src/service/index.ts create mode 100644 tsconfig.json diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..5171c54 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +node_modules +npm-debug.log \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c70a64a --- /dev/null +++ b/.gitignore @@ -0,0 +1,47 @@ +.idea +*.iml + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +build/ + +# Dependency directories +node_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Output of 'npm pack' +*.tgz + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +config/ +!config/settings.sample.yaml \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d287603 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM node:20-alpine +ENV NODE_ENV=development + +WORKDIR /usr/app/ +COPY build/ . +COPY package*.json ./ +RUN npm install + +ENV NODE_ENV production + +EXPOSE 5056 +CMD ["node", "index.js"] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..a4b39da7c070e359282c7a2a2d79c6aaf685c950 GIT binary patch literal 2236 zcmai#OK)01428X})c-JIl}brkRb6#Q$P1N6L?CIFKp@bPfFLx=pKp6U`wkaq)e7C* z8PBnOeC#{<^|NVp>sqpgO>Jl+d$N`OzO^_0)NF1Gd$pxbA8caJLLLbBPVbNQLC+^U z5@IPI{GJ@K(E8QB%lF8=lJ#|RFQ2*2C&DeY9%=`k*aKT@HP?Ez$$jOxN1d+pn>srA zE)_9G@_SXJ2lYz z+Ow89D(V8YMlJV3_2maUm}3vmeZM4@Q$L-%>dCcQ$wDWFdIu9?6KSZZYcXbx7W>{a zozSJ-DsuL6a-y@rcCG576HEcBlI>Az@{Bio`jUIL5Q~Ahp!me`U^DNSpoqEJAj^p+ ze)QzMvM_%zm=%fzi>WNAUUnDd_w_3yW$j~rLWA9DB3e}ZBF=MF_^-ZD4gDL7|59p4 zrOb4W!ra8P!6NGXCf0N1ogULbj74 zV@rNzotG&@Wl;2jU7izE zZaaU}%5xf7h?luUMmoIBCeL?P{BK`NM9GuZb0&XsOs+ir3ufpSoeodf?SF~!t^Nj( CMq)Jp literal 0 HcmV?d00001 diff --git a/README.md b/README.md new file mode 100644 index 0000000..46c7d2a --- /dev/null +++ b/README.md @@ -0,0 +1,276 @@ + +## Overcrawlrr + +CRON based bot that automatically requests movies you may like in Overseer using your own rules. + +```yaml + # Accept Sci-fi movie with a global rating above 5 + rules: + - name: Acceptable sci-fi movie + whenMatch: + - genres: + - science-fiction + - score: above 5 + action: accept +``` + +Requirement: +- a running instance of [Overseerr](https://overseerr.dev/) + +## Installation + +## Configuration + +### Configure Overseerr + +An Overseerr user must be configured to post requests. + +It is recommended to create a [local user in Overseerr](https://docs.overseerr.dev/using-overseerr/users#creating-local-users) for Overcrawlrr. +When testing your rules, you can disable Overseerr automatic validations for this user. +You'll need to validate the requests in Overseerr, allowing for a double check on your rules. + +```yaml + config: + overseerr: + apiUrl: xxx + user: xxx + password: xxx + dryRun: true # Optional - dryRun will not send requests to Overseerr allowing you to test your rules +``` + +### Configure the cron + +It's time to configure the discovery job. It will fetch movies from Overseerr (from the upcoming/popular/trending sections) and evaluate each of them. + +```yaml +config: + overseer: + # .... + discovery: + # Required + cron: '? 30 3 * * *' + # Required - the overseer streams to search through + # Possible values: upcoming, popular, trending + streams: + - upcoming + - popular + - trending + # Required - the name of the ruleset used to evaluate movies + ruleset: Ruleset name +``` + +## Rulesets + +A ruleset defines a group of rules, used to detect if a movie should be requested or not. + +```yaml +config: + # ... + rulesets: + # Required - the name of the ruleset + - name: Ruleset name + # Optional - the name of another ruleset. This ruleset will apply all the rules in the extended rulese + # before applying its own rules + extends: Another ruleset + # Required - An array defining the rules of the ruleset + rules: + - # rule 1 + - # rule 2 + - # rule ... +``` + +- The rules are applied in the declared order, +- When a rule matches, the rule `action` is applied, and the evaluation of the ruleset stops. +- If a rule doesn't match, it is ignored, and the next rule is evaluated. + +### Rule definition + +```yaml + # Required - the name of the rule + - name: The rule name + # Required - an array defining the predicates used in the rule + whenMatch: + - # predicate 1 + - # predicate 2 + - # ... + - # predicate n + # Required - the action to apply when the rule matches + # Possible values: accept or reject + action: accept +``` + +The rule will match if **all the predicates** in the `whenMatch` match. + +When matching: +- If `action: accept`, a request is sent to Overseerr to add the movie, +- If `action: reject`, the movie is ignored, and the evaluation of the ruleset stops. + +### Rule predicates + +--- +#### `released` + +Filters on the released status of the movie. + +```yaml + - released: yes + # or + - released: no +``` + +--- +#### `age` + +Filters on the age of the movie. + +```yaml + - age: less than 2 years + # or + - age: more than 6 months +``` + +--- +#### `score` + +Filters on the score of the movie. + +```yaml + - score: above 6.5 + # or + - score: below 5.5 +``` + +--- +#### `voteCount` + +Filters on the vote count of the movie. + + +```yaml + - voteCount: above 1000 + # or + - voteCount: below 100 +``` + +--- +#### `genre` + +Filters on the genre of the movie. Will match when one or more of the listed genres matches the genre of the movie. + +**Case insensitive** + +```yaml + - genres: + - animation + - romance +``` + +--- +#### `watchProviders` + +Filters based on the available Streaming/VOD platforms. Will match when one or more of the listed provider matches. + +**Case insensitive** + +```yaml + # This predicate will match when the movie is available in Germany on Netflix or Amazon Prime + - watchProviders: + - region: de + - names: + - Netflix + - Amazon Prime +``` + +--- +#### `cast` + +Filters based on the cast of the movie. Will match when one or more of the listed name matches. + +**Case insensitive** + +```yaml + - cast: + - Denzel Washington + - Jessica Alba +``` + +--- +#### `crew` + +Filters based on the crew of the movie. Will match when one or more of the listed name matches. + +**Case insensitive** + +```yaml + - crew: + - James Cameron + - Hans Zimmer +``` + +It is also possible to specify the job +```yaml + - crew: + job: director + names: + - James Cameron + - Steven Spielberg +``` +--- +#### `productionCompany` + +Filters based on the production companies of the movie. Will match when one or more of the listed company matches. + +**Case insensitive** + +```yaml + - productionCompany: + - 20th Century Fox + - Warner Bros. Pictures + - Twisted Pictures +``` + +--- +#### `or` + +Predicate that will match if any of its predicate matches + +```yaml + # Will match if the movie is less than 2 years old OR if the movie score is above 8 + - or: + - age: less than 2 years + - score: above 8 +``` + +--- +#### `and` + +Predicate that will match if all of its predicate matches + +```yaml + # Will match if the movie is less than 2 years old AND if the movie genre is 'animation' + - and: + - age: less than 2 years + - genres: + - animation +``` + +--- + +--- +#### `not` + +Predicate that invert the result of its child predicate + +```yaml + - not: + - genres: + - animation +``` + +--- + + + + + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..0234690 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5667 @@ +{ + "name": "overcrawlrr", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "overcrawlrr", + "version": "0.0.1", + "license": "MIT", + "dependencies": { + "@types/express": "^4.17.17", + "@types/js-yaml": "^4.0.5", + "ajv": "^8.17.1", + "axios": "^1.4.0", + "cookie": "^0.5.0", + "cors": "^2.8.5", + "croner": "^6.0.6", + "dotenv": "^16.3.1", + "express": "^4.18.2", + "js-yaml": "^4.1.0", + "node-cache": "^5.1.2", + "tsx": "^3.12.7", + "winston": "^3.9.0", + "xml2js": "^0.6.2" + }, + "devDependencies": { + "@types/cookie": "^0.5.1", + "@types/cors": "^2.8.13", + "@types/jest": "^29.5.2", + "@types/node": "^20.4.1", + "@types/xml2js": "^0.4.14", + "jest": "^29.6.1", + "ts-jest": "^29.1.1", + "tsc-alias": "^1.8.7", + "typescript": "^5.1.6" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.24.7", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.2.tgz", + "integrity": "sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/debug": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/core/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/@babel/generator": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", + "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.25.2", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", + "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", + "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-simple-access": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", + "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", + "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/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": ">=4" + } + }, + "node_modules/@babel/highlight/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/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/@babel/highlight/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/@babel/highlight/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/@babel/highlight/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/@babel/highlight/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": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", + "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.2" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", + "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", + "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz", + "integrity": "sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", + "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.25.0", + "@babel/types": "^7.25.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz", + "integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/parser": "^7.25.3", + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.2", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/traverse/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/@babel/types": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", + "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "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" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "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/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cookie": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.5.4.tgz", + "integrity": "sha512-7z/eR6O859gyWIAjuvBWFzNURmf2oPBmJlfVWkwehU5nzIyjwBsTh7WMmEEV4JFnHuQ3ex4oyTvfKzcyJVDBNA==", + "dev": true + }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.5", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz", + "integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.12", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", + "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" + }, + "node_modules/@types/node": { + "version": "20.16.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.1.tgz", + "integrity": "sha512-zJDo7wEadFtSyNz5QITDfRcrhqDvQI1xQNQ0VoizPjM/dVAODqqIUWbJPkvsxmTI0MYRGRikcdjMPhOssnPejQ==", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/qs": { + "version": "6.9.15", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", + "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + }, + "node_modules/@types/xml2js": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.14.tgz", + "integrity": "sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "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/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "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==" + }, + "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/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + }, + "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.7.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", + "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "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==" + }, + "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.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001651", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz", + "integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", + "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", + "dev": true + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/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==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/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==" + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "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/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "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/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "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/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/croner": { + "version": "6.0.7", + "resolved": "https://registry.npmjs.org/croner/-/croner-6.0.7.tgz", + "integrity": "sha512-k3Xx3Rcclfr60Yx4TmvsF3Yscuiql8LSvYLaphTsaq5Hk8La4Z/udmUANMOTKpgGGroI2F6/XOr9cU9OFkYluQ==", + "engines": { + "node": ">=6.0" + } + }, + "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/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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", + "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/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "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/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.11", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.11.tgz", + "integrity": "sha512-R1CccCDYqndR25CaXFd6hp/u9RaaMcftMkphmvuepXr5b1vfLkRml6aWVeBhXJ7rbevHkKEMJtz8XqPf7ffmew==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, + "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/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "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/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/express": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "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/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-uri": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", + "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==" + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/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/filelist/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/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "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", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "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", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "4.7.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.6.tgz", + "integrity": "sha512-ZAqrLlu18NbDdRaHq+AKXzAmqIUPswPWKUchfytdAjiRFnCe5ojG2bstg6mRiZabkKfCoL/e98pbBELIV/YCeA==", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "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-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", + "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/debug": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/istanbul-lib-source-maps/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/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "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==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "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.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/logform": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.1.tgz", + "integrity": "sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "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": { + "yallist": "^3.0.2" + } + }, + "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": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "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", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "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", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "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==", + "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==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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, + "engines": { + "node": ">=6" + } + }, + "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": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/mylas": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/mylas/-/mylas-2.1.13.tgz", + "integrity": "sha512-+MrqnJRtxdF+xngFfUUkIMQrUUL0KsxbADUkn23Z/4ibGg192Q+z+CQyiYwvWTsYjJygmMR8+w3ZDa98Zh6ESg==", + "dev": true, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/raouldeheer" + } + }, + "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/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-cache": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", + "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", + "dependencies": { + "clone": "2.x" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "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.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "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/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, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "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": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "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": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "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==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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": ">=8" + }, + "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", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "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, + "engines": { + "node": ">=8" + } + }, + "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, + "engines": { + "node": ">=0.10.0" + } + }, + "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-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/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dev": true + }, + "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/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": ">= 6" + } + }, + "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": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/plimit-lit": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/plimit-lit/-/plimit-lit-1.6.1.tgz", + "integrity": "sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==", + "dev": true, + "dependencies": { + "queue-lit": "^1.5.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "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/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-lit": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/queue-lit/-/queue-lit-1.5.2.tgz", + "integrity": "sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "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", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=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==", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "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", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "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.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "engines": { + "node": "*" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "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", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/ts-jest": { + "version": "29.2.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.4.tgz", + "integrity": "sha512-3d6tgDyhCI29HlpwIq87sNuI+3Q6GLTTCeYRHCs7vDz+/3GCMwEtV9jezLyl4ZtnBgx00I7hm8PCP8cTksMGrw==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "ejs": "^3.1.10", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "^7.5.3", + "yargs-parser": "^21.0.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tsc-alias": { + "version": "1.8.10", + "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.8.10.tgz", + "integrity": "sha512-Ibv4KAWfFkFdKJxnWfVtdOmB0Zi1RJVxcbPGiCDsFpCQSsmpWyuzHG3rQyI5YkobWwxFPEyQfu1hdo4qLG2zPw==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.3", + "commander": "^9.0.0", + "globby": "^11.0.4", + "mylas": "^2.1.9", + "normalize-path": "^3.0.0", + "plimit-lit": "^1.2.6" + }, + "bin": { + "tsc-alias": "dist/bin/index.js" + } + }, + "node_modules/tsx": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-3.14.0.tgz", + "integrity": "sha512-xHtFaKtHxM9LOklMmJdI3BEnQq/D5F73Of2E1GDrITi9sgoVkvIsrQUTY1G8FlmGtA+awCI4EBlTRRYxkL2sRg==", + "dependencies": { + "esbuild": "~0.18.20", + "get-tsconfig": "^4.7.2", + "source-map-support": "^0.5.21" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tsx/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==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.19.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.6.tgz", + "integrity": "sha512-e/vggGopEfTKSvj4ihnOLTsqhrKRN3LeO6qSN/GxohhuRv8qH9bNQ4B8W7e/vFL+0XTnmHPB4/kegunZGA4Org==" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.2", + "picocolors": "^1.0.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "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": ">= 8" + } + }, + "node_modules/winston": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.14.2.tgz", + "integrity": "sha512-CO8cdpBB2yqzEf8v895L+GNKYJiEq8eKlHU38af3snQBQ+sdAIUepjMSguOIJC7ICbzm0ZI+Af2If4vIJrtmOg==", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.6.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.7.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.1.tgz", + "integrity": "sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==", + "dependencies": { + "logform": "^2.6.1", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..2197af3 --- /dev/null +++ b/package.json @@ -0,0 +1,54 @@ +{ + "name": "overcrawlrr", + "version": "0.0.1", + "description": "", + "main": "./build/index.js", + "files": [ + ".build/**/*" + ], + "scripts": { + "build": "tsc && tsc-alias", + "postbuild": "cp -r ./schema/ ./build/", + "start": "tsx src/index.ts", + "start:watch": "tsx watch src/index.ts", + "test:unit": "jest" + }, + "author": "psyko-gh", + "license": "MIT", + "devDependencies": { + "@types/cookie": "^0.5.1", + "@types/cors": "^2.8.13", + "@types/jest": "^29.5.2", + "@types/node": "^20.4.1", + "@types/xml2js": "^0.4.14", + "jest": "^29.6.1", + "ts-jest": "^29.1.1", + "tsc-alias": "^1.8.7", + "typescript": "^5.1.6" + }, + "dependencies": { + "@types/express": "^4.17.17", + "@types/js-yaml": "^4.0.5", + "ajv": "^8.17.1", + "axios": "^1.4.0", + "cookie": "^0.5.0", + "cors": "^2.8.5", + "croner": "^6.0.6", + "dotenv": "^16.3.1", + "express": "^4.18.2", + "js-yaml": "^4.1.0", + "node-cache": "^5.1.2", + "tsx": "^3.12.7", + "winston": "^3.9.0", + "xml2js": "^0.6.2" + }, + "jest": { + "preset": "ts-jest", + "testEnvironment": "node", + "moduleNameMapper": { + "^@core/(.*)$": [ + "/src/$1" + ] + } + } +} diff --git a/schema/schema.json b/schema/schema.json new file mode 100644 index 0000000..f5b9386 --- /dev/null +++ b/schema/schema.json @@ -0,0 +1,235 @@ +{ + "type": "object", + "properties": { + "config": { + "type": "object", + "required": ["overseerr", "discovery", "rulesets"], + "properties": { + "overseerr": { "$ref": "#/definitions/overseer" }, + "plex": { "$ref": "#/definitions/plex" }, + "discovery": { "$ref": "#/definitions/discovery"}, + "smartRecommendations": { "$ref": "#/definitions/smartRecommendations"}, + "rulesets": { "$ref": "#/definitions/rulesets"} + } + } + }, + "definitions": { + + "overseer": { + "$id": "#/definitions/overseer", + "type": "object", + "required": ["apiUrl", "user", "password"], + "properties": { + "apiUrl": { "type": "string" }, + "user": { "type": "string" }, + "password": { "type": "string" }, + "dryRunUrl": { "type": "boolean" } + } + }, + + "plex": { + "$id": "#/definitions/plex", + "type": "object", + "required": ["apiUrl", "plexToken"], + "properties": { + "apiUrl": { "type": "string" }, + "plexToken": { "type": "string" } + } + }, + + "discovery": { + "$id": "#/definitions/discovery", + "type": "object", + "required": ["cron", "streams", "ruleset"], + "properties": { + "cron": { "type": "string" }, + "ruleset": { "type": "string" }, + "streams": { "type": "array", "items": { "type": "string" }} + } + }, + + "smartRecommendations": { + "$id": "#/definitions/smartRecommendations", + "type": "object", + "required": ["cron", "plexLibrary", "ruleset", "minimumRating"], + "properties": { + "cron": { "type": "string" }, + "plexLibrary": { "type": "string" }, + "ruleset": { "type": "string" }, + "minimumRating": { "type": "number"} + } + }, + + "rulesets": { + "$id": "#/definitions/rulesets", + "type": "array", + "items": { "$ref": "#/definitions/ruleset" } + }, + + "ruleset": { + "$id": "#/definitions/ruleset", + "type": "object", + "required": ["name", "rules"], + "properties": { + "name": { "type": "string" }, + "extends": { "type": "string" }, + "rules": { "type": "array", "items": { "$ref": "#/definitions/rule" }} + } + }, + + "rule": { + "$id": "#/definitions/rule", + "type": "object", + "required": ["name", "whenMatch", "action"], + "properties": { + "name": { "type": "string" }, + "whenMatch": { "type": "array", "items": { "$ref": "#/definitions/predicate" }}, + "action": { "type": "string", "enum": ["accept", "reject", "skip"]} + } + }, + + "predicate": { + "$id": "#/definitions/predicate", + "type": "object", + "oneOf": [ + { "$ref": "#/definitions/orPredicate" }, + { "$ref": "#/definitions/andPredicate" }, + { "$ref": "#/definitions/notPredicate" }, + { "$ref": "#/definitions/agePredicate" }, + { "$ref": "#/definitions/scorePredicate" }, + { "$ref": "#/definitions/genrePredicate" }, + { "$ref": "#/definitions/watchProviderPredicate" }, + { "$ref": "#/definitions/voteCountPredicate" }, + { "$ref": "#/definitions/castPredicate" }, + { "$ref": "#/definitions/crewPredicate" }, + { "$ref": "#/definitions/releasedPredicate" }, + { "$ref": "#/definitions/productionCompanyPredicate" } + ] + }, + + "orPredicate": { + "$id": "#/definitions/orPredicate", + "type": "object", + "required": ["or"], + "properties": { + "or": { "type": "array", "items": { "$ref": "#/definitions/predicate" }} + } + }, + + "andPredicate": { + "$id": "#/definitions/andPredicate", + "type": "object", + "required": ["and"], + "properties": { + "and": { "type": "array", "items": { "$ref": "#/definitions/predicate" }} + } + }, + + "notPredicate": { + "$id": "#/definitions/notPredicate", + "type": "object", + "required": ["not"], + "properties": { + "not": { "type": "array", "items": { "$ref": "#/definitions/predicate" }} + } + }, + + "agePredicate": { + "$id": "#/definitions/agePredicate", + "type": "object", + "required": ["age"], + "properties": { + "age": { "type": "string" } + } + }, + + "releasedPredicate": { + "$id": "#/definitions/releasedPredicate", + "type": "object", + "required": ["released"], + "properties": { + "released": { "type": "string", "enum": ["yes", "no"]} + } + }, + + "scorePredicate": { + "$id": "#/definitions/scorePredicate", + "type": "object", + "required": ["score"], + "properties": { + "score": { "type": "string" } + } + }, + + "voteCountPredicate": { + "$id": "#/definitions/voteCountPredicate", + "type": "object", + "required": ["voteCount"], + "properties": { + "voteCount": { "type": "string" } + } + }, + + "genrePredicate": { + "$id": "#/definitions/genrePredicate", + "type": "object", + "required": ["genres"], + "properties": { + "genres": { "type": "array", "items": { "type": "string" }} + } + }, + + "watchProviderPredicate": { + "$id": "#/definitions/watchProviderPredicate", + "type": "object", + "required": ["watchProviders"], + "properties": { + "watchProviders": { + "type": "object", + "properties": { + "region": { "type": "string"}, + "names": { "type": "array", "items": { "type": "string" }} + } + } + } + }, + + "castPredicate": { + "$id": "#/definitions/castPredicate", + "type": "object", + "required": ["cast"], + "properties": { + "cast": { "type": "array", "items": {"type": "string" }} + } + }, + + "crewPredicate": { + "$id": "#/definitions/crewPredicate", + "type": "object", + "required": ["crew"], + "properties": { + "crew": { + "oneOf": [ + { "type": "array", "items": { "type": "string" }}, + { + "type": "object", + "properties": { + "job": { "type": "string"}, + "names": { "type": "array", "items": {"type": "string" }} + } + } + ] + } + } + }, + + "productionCompanyPredicate": { + "$id": "#/definitions/productionCompanyPredicate", + "type": "object", + "required": ["productionCompany"], + "properties": { + "productionCompany": { "type": "array", "items": {"type": "string" }} + } + } + } +} \ No newline at end of file diff --git a/src/api/httpApi.ts b/src/api/httpApi.ts new file mode 100644 index 0000000..36a4c29 --- /dev/null +++ b/src/api/httpApi.ts @@ -0,0 +1,143 @@ +import type {AxiosInstance, AxiosRequestConfig, InternalAxiosRequestConfig} from "axios"; +import axios from "axios"; +import NodeCache from "node-cache"; +import logger from "@core/log"; +import cookie from 'cookie'; + +const DEFAULT_TTL = 100; +const COOKIE_NAME = 'connect.sid'; + +interface HttpApiOptions { + cache?: NodeCache; + headers?: Record; +} + +class HttpApi { + protected axios: AxiosInstance; + private cookie:string; + private baseUrl: string; + private cache?: NodeCache; + + constructor( + baseUrl: string, + params: Record, + options: HttpApiOptions = {}, + debug: Boolean = false + ) { + this.cookie = ''; + this.baseUrl = baseUrl; + this.cache = options.cache; + + this.axios = axios.create({ + baseURL: baseUrl, + withCredentials: true, + params, + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + ...options.headers + } + }); + this.activateCookieInterceptor(); + if (debug) { + this.enableDebug(); + } + } + + protected async get( + endpoint: string, + config?: AxiosRequestConfig, + ttl?: number + ): Promise { + const cacheKey = this.getCacheKey(endpoint, config?.params); + const cachedResult = this.cache?.get(cacheKey); + if (cachedResult) { + logger.debug('Served from cache ' + cacheKey) + return cachedResult; + } + + const response = await this.axios.get(endpoint, config); + + if (this.cache) { + this.cache.set(cacheKey, response.data, ttl ?? DEFAULT_TTL); + } + + return response.data; + } + + protected async post( + endpoint: string, + data: Record, + config?: AxiosRequestConfig, + ttl?: number + ): Promise { + const cacheKey = this.getCacheKey(endpoint, config?.params); + // const cachedResult = this.cache?.get(cacheKey); + // if (cachedResult) { + // return cachedResult; + // } + + const response = await this.axios.post(endpoint, data, config); + + // if (this.cache) { + // this.cache.set(cacheKey, response.data, ttl ?? DEFAULT_TTL); + // } + + return response.data; + } + + private getCacheKey( + endpoint: string, + params: Record + ): string { + if (!params) { + return `${this.baseUrl}${endpoint}`; + } + return `${this.baseUrl}${endpoint}${JSON.stringify(params)}`; + } + + private activateCookieInterceptor() { + this.axios.interceptors.request.use((req: InternalAxiosRequestConfig) => { + if (this.cookie) { + req.headers.cookie = cookie.serialize(COOKIE_NAME, this.cookie); + } + return req; + }); + + this.axios.interceptors.response.use((res) => { + if (res.headers['set-cookie']) { + const cookies = res.headers['set-cookie']as string[]; + const c = cookie.parse(cookies.shift() ?? ''); + this.cookie = c[COOKIE_NAME]; + } + return res; + }); + } + + private enableDebug() { + this.axios.interceptors.request.use((x: InternalAxiosRequestConfig) => { + const method = x.method ?? ''; + const headers = { + ... x.headers.common, + ... x.headers[method], + ... x.headers + } + + // remove irrelevant headers + const removed = ['common', 'get', 'post', 'head', 'put', 'patch', 'delete']; + removed.forEach(h => delete headers[h]); + + const printable = `Request: ${method.toUpperCase()} ${x.baseURL} | ${x.url} | ${Object.keys(x.params).map(k => `${k}: ${x.params[k]}`).join(', ')} | ${JSON.stringify(x.data)} | ${JSON.stringify(x.headers)}` + logger.debug(printable); + + return x; + }); + + this.axios.interceptors.response.use(x => { + logger.debug(`Response: ${x.status} | ${JSON.stringify(x.data)} | ${JSON.stringify(x.headers)}`); + return x; + }); + } +} + +export default HttpApi; \ No newline at end of file diff --git a/src/api/overseerr/index.ts b/src/api/overseerr/index.ts new file mode 100644 index 0000000..7389767 --- /dev/null +++ b/src/api/overseerr/index.ts @@ -0,0 +1,81 @@ +import HttpApi from "@core/api/httpApi"; +import {getSettings} from "@core/lib/settings"; +import logger from "@core/log"; +import { + AuthResponse, MediaRequest, + MovieDetails, + MovieResult, + OverseerrRequestsResult, + OverseerrResult +} from "@core/api/overseerr/interfaces"; +import {color, Icon} from "@core/lib/utils"; + +class OverseerrApi extends HttpApi { + + constructor() { + super( + getSettings().overseerr.apiUrl, + {}); + } + + public test = async () => { + try { + logger.info('Testing overseerr connection...'); + await this.auth(); + logger.info(color.green(Icon.CHECK) + ' Overseerr connection successful'); + } catch (e) { + logger.error('Error while testing Overseerr connection. Verify URL and credentials: ', e); + } + } + + public auth = async () => { + const settings = getSettings().load().overseerr; + return await this.post(`/auth/local`, { + email: settings.user, + password: settings.password + }); + } + + public getRequests = async (): Promise => { + return (await this.get(`/request`, + { + params: { + take: 100 + } + }, + 1)).results + } + + public getUpcoming = async (): Promise => { + return (await this.get>(`/discover/movies/upcoming`, {}, 1)).results + } + + public getMovie = async (idMovie: number): Promise => { + return await this.get(`/movie/${idMovie}`) + } + + public getPopular = async (): Promise => { + return (await this.get>(`/discover/movies`)).results + } + + public getTrending = async (): Promise => { + return (await this.get>(`/discover/trending`)).results + } + + public getRecommendationsByMovie = async (idMovie: string | number): Promise => { + return (await this.get>(`/movie/${idMovie}/recommendations`)).results + } + + public requestMovie = async (idMovie: number) => { + try { + return await this.post(`/request`, { + mediaType: 'movie', + mediaId: idMovie + }) + } catch (e) { + logger.error(e); + } + } +} + +export default OverseerrApi; \ No newline at end of file diff --git a/src/api/overseerr/interfaces.ts b/src/api/overseerr/interfaces.ts new file mode 100644 index 0000000..2045143 --- /dev/null +++ b/src/api/overseerr/interfaces.ts @@ -0,0 +1,184 @@ +export type MediaType = 'tv' | 'movie' | 'person' | 'collection'; + +export interface AuthResponse { + id: number; + permissions: number; + password: string; + requestCount: number; + displayName: string; +} + +export enum MediaStatus { + UNKNOWN = 1, + PENDING, + PROCESSING, + PARTIALLY_AVAILABLE, + AVAILABLE, +} + +interface SearchResult { + id: number; + mediaType: MediaType; + popularity: number; + posterPath?: string; + backdropPath?: string; + voteCount: number; + voteAverage: number; + genreIds: number[]; + overview: string; + originalLanguage: string; + mediaInfo?: Media; +} + +export interface Media { + id: number; + mediaType: MediaType; + tmdbId?: number | null; + tvdbId?: number | null; + imdbId?: string | null; + status: MediaStatus; + createdAt: string; + requests?: MediaRequest[]; +} + +export interface OverseerrResult { + page: number; + totalResults: number; + totalPages: number; + results: T[]; +} + +export interface MovieResult extends SearchResult { + mediaType: 'movie'; + title: string; + originalTitle: string; + releaseDate: string; + adult: boolean; + video: boolean; + mediaInfo?: Media; +} + +export interface MovieDetails { + id: number; + imdbId?: string; + adult: boolean; + // backdropPath?: string; + // posterPath?: string; + budget: number; + genres: Genre[]; + homepage?: string; + originalLanguage: string; + originalTitle: string; + overview?: string; + popularity: number; + productionCompanies: ProductionCompany[]; + productionCountries: { + iso_3166_1: string; + name: string; + }[]; + releaseDate: string; + // release_dates: TmdbMovieReleaseResult; + revenue: number; + runtime?: number; + spokenLanguages: { + english_name: string; + iso_639_1: string; + name: string; + }[]; + status: string; + tagline?: string; + title: string; + video: boolean; + voteAverage: number; + voteCount: number; + credits: { + cast: CreditCast[]; + crew: CreditCrew[]; + }; + collection?: { + id: number; + name: string; + posterPath?: string; + backdropPath?: string; + }; + watchProviders?: WatchProvidersRegion[]; + keywords: { + id: number; + name: string; + }[]; + mediaInfo?: Media; +} + +export interface WatchProvidersRegion { + iso_3166_1: string; + link?: string; + buy?: WatchProviderDetails[]; + flatrate?: WatchProviderDetails[]; +} + +export interface WatchProviderDetails { + displayPriority?: number; + logoPath?: string; + id: number; + name: string; +} + +export interface TmdbKeyword { + id: number; + name: string; +} + +export enum RequestStatus { + PENDING = 1, + APPROVED, + DECLINED +} + +export interface MediaRequest { + id: number; + status: RequestStatus; + media: Media; + createdAt: string; + updatedAt: string; + is4k: boolean; + serverId: number; + profileId: number; + rootFolder: string; +} + +export interface OverseerrRequestsResult { + page: number; + totalResults: number; + totalPages: number; + results: MediaRequest[]; +} + +export interface CreditCast { + id: number; + castId: number; + character: string; + creditId: string; + gender: number; + name: string; + order: number; +} + +export interface CreditCrew { + id: number; + gender: number; + name: string; + job: string; + department: string; +} + +export interface ProductionCompany { + id: number; + logoPath: string; + originCountry: string; + name: string; +} + +export interface Genre { + id: number; + name: string; +} diff --git a/src/api/plex/index.ts b/src/api/plex/index.ts new file mode 100644 index 0000000..2333ca4 --- /dev/null +++ b/src/api/plex/index.ts @@ -0,0 +1,99 @@ +import HttpApi from "@core/api/httpApi"; +import {getSettings} from "@core/lib/settings"; +import logger from "@core/log"; +import {Parser} from "xml2js"; +import type {AxiosRequestConfig} from "axios"; +import {PlexSectionDirectory, PlexSectionsResponse, PlexVideo, PlexVideosResponse} from "@core/api/plex/interfaces"; +import {color, Icon, isFulfilled} from "@core/lib/utils"; + +class PlexApi extends HttpApi { + xmlParser; + + constructor() { + super( + getSettings().plex?.apiUrl ?? '', + {}, + { + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/xml', + } + } + ); + this.xmlParser = new Parser({ + mergeAttrs: true, + explicitArray: false + }); + } + + protected async get( + endpoint: string, + config?: AxiosRequestConfig, + ttl?: number + ): Promise { + const result = await super.get( + endpoint, + { + ...config, + params: { + 'X-Plex-Token': getSettings().plex?.plexToken ?? '', + ...config?.params + } + }, + ttl + ) + const data = await this.xmlParser.parseStringPromise(result as string); + return data.MediaContainer ? data.MediaContainer : data; + } + + public test = async () => { + try { + logger.info('Testing Plex connection...'); + await this.get('/'); + logger.info(color.green(Icon.CHECK) + ' Plex connection successful'); + } catch (e) { + logger.error('Error while testing Plex connection. Verify URL and credentials: ', e); + } + } + + public getMovie = async (idMovie: number | string) => { + return await this.get(`/library/metadata/${idMovie}`, { + params: { + 'include_guids': 1 + } + }) + } + + public getLibrary = async (names: string) : Promise => { + const libs = await this.get('/library/sections') + return libs.Directory.find(d => names.includes(d.title)); + } + + public getGuid = (provider: string, video: PlexVideo) => { + if (!video['Guid']) { + return null; + } + const prefix = `${provider}://`; + const guids = video.Guid ? Array.from(video.Guid) : []; + const guid = guids.find(g => g.id.startsWith(prefix)); + if (!guid) { + return null; + } + return guid.id.substring(prefix.length) + } + + public getFavoritesRecommendationsByLibrary = async(sectionKey: string, score: number) => { + const favorites = await this.get(`/library/sections/${sectionKey}/all`, { + params: { + 'userRating>': score + } + }) + const moviesResults: Array> = await Promise.allSettled( + favorites.Video.map(v => this.getMovie(v.ratingKey)) + ) + return moviesResults.filter(isFulfilled) + .flatMap(r => r.value.Video); + } +} + +export default PlexApi; \ No newline at end of file diff --git a/src/api/plex/interfaces.ts b/src/api/plex/interfaces.ts new file mode 100644 index 0000000..baf9bbe --- /dev/null +++ b/src/api/plex/interfaces.ts @@ -0,0 +1,44 @@ +export interface PlexMediaContainer { + MediaContainer: T +} + +export interface PlexSectionsResponse { + Directory: PlexSectionDirectory[]; +} + +export interface PlexSectionDirectory { + art: string; + composite: string; + // filters: string; + // refreshing: '0', + thumb: string; + key: string; + type: string; + title: string; + // agent: 'tv.plex.agents.music', + // scanner: 'Plex Music', + // language: 'en-US', + uuid: string; + // updatedAt: '1723454062', + // createdAt: '1688070309', + // scannedAt: '1703967043', + // content: '1', + // directory: '1', + // contentChangedAt: '7726555702310039813', + // hidden: '0', +} + +export interface PlexVideosResponse { + Video: PlexVideo[]; +} + +export interface PlexVideo { + ratingKey: string, + Guid?: PlexGuid[], + title: string; + originalTitle: string; +} + +export interface PlexGuid { + id: string; +} \ No newline at end of file diff --git a/src/env.ts b/src/env.ts new file mode 100644 index 0000000..63e37ad --- /dev/null +++ b/src/env.ts @@ -0,0 +1,17 @@ +export default function envVar( + name: string, + defaultValue?: string | number | boolean | undefined, +): string | number | boolean { + const val = process.env[name] + if (typeof val === 'undefined') { + if (defaultValue !== undefined) return defaultValue + throw new Error(`Environment variable '${name}' does not exist, value: ${JSON.stringify(process.env)}`) + } + const stringVal = String(val).trim() + if (stringVal.toLowerCase() === 'true' || stringVal.toLowerCase() === 'false') { + return stringVal === 'true' + } + + const numVal = Number(stringVal) + return Number.isNaN(numVal) ? stringVal : numVal +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..c525836 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,54 @@ +import dotenv from 'dotenv' +import express from 'express'; +import cors from 'cors'; +import envVar from "@core/env"; +import logger from "@core/log"; +import Settings, {getSettings, OnSettingLoadedCallback} from "@core/lib/settings"; +import apiRouter from "@core/routes"; +import {loadRulesets} from "@core/lib/ruleset"; +import {registerCrons} from "@core/lib/cron"; +import OverseerrApi from "@core/api/overseerr"; +import PlexApi from "@core/api/plex"; +import fs from "fs"; + +dotenv.config() + +const app = express(); +const port = envVar('PORT', 5056); + +const corsOptions = { + origin: [ + `http://localhost`, + `http://localhost:${port}`, + `http://127.0.0.1`, + `http://127.0.0.1:${port}` + ], + credentials: true, + exposedHeaders: ['set-cookie'], +}; + +app.use(cors(corsOptions)); +app.use('/api', apiRouter); + +app.listen(port, () => { + return logger.info(`Server is listening at http://localhost:${port}`); +}); + +const onLoad: OnSettingLoadedCallback = async (settings: Settings) => { + loadRulesets(settings); + registerCrons(settings); + + // Testing settings + const overseerr = new OverseerrApi(); + await overseerr.test(); + + if (settings.plex) { + const plex = new PlexApi(); + await plex.test(); + } +}; + +getSettings() + .watch(onLoad) + .load(); + diff --git a/src/lib/cron.ts b/src/lib/cron.ts new file mode 100644 index 0000000..3ba887c --- /dev/null +++ b/src/lib/cron.ts @@ -0,0 +1,56 @@ +import {Cron, CronOptions} from "croner"; +import {discover, smartRecommendations} from "@core/service"; +import logger from "@core/log"; +import Settings, {getSettings} from "@core/lib/settings"; + +interface EnabledFunction { + (): boolean; +} + +interface ProcessFunction { + (): Promise; +} + +interface JobDefinition { + name: string; + cronExpression: string; + isEnabled: EnabledFunction; + process: ProcessFunction; +} + +const jobDefinitionsGetter = (settings: Settings) => [ + { + name: 'Discover job', + cronExpression: settings.load().discovery.cron ?? '', + isEnabled: () => !!settings.load().discovery.cron, + process: async () => discover() + }, + { + name: 'Smart recommendations job', + cronExpression: settings.load().smartRecommendations?.cron ?? '', + isEnabled: () => !!settings.load().smartRecommendations, + process: async () => smartRecommendations() + } +]; + +const jobs: Cron[] = []; +export function registerCrons(settings: Settings) { + jobs.forEach(j => j.stop()); + jobDefinitionsGetter(settings).forEach((jobDefinition: JobDefinition) => { + if (!jobDefinition.isEnabled()) { + return; + } + const options: CronOptions = { + name: jobDefinition.name, + protect: true, + } + + const cron = Cron(jobDefinition.cronExpression, options, async () => { + logger.info(`----- Starting : ${jobDefinition.name} ! ----- `); + await jobDefinition.process?.(); + logger.info(`----- completeD: ${jobDefinition.name} ! ----- `); + }); + jobs.push(cron); + logger.info(`Registered ${jobDefinition.name}. Next run: ` + cron.nextRun()) + }); +} \ No newline at end of file diff --git a/src/lib/rules/__tests__/movie.json b/src/lib/rules/__tests__/movie.json new file mode 100644 index 0000000..7f5a8f0 --- /dev/null +++ b/src/lib/rules/__tests__/movie.json @@ -0,0 +1,4131 @@ +{ + "id": 679, + "adult": false, + "budget": 18500000, + "genres": [ + { + "id": 28, + "name": "Action" + }, + { + "id": 53, + "name": "Thriller" + }, + { + "id": 878, + "name": "Science-Fiction" + } + ], + "relatedVideos": [ + { + "site": "YouTube", + "key": "X4xLFXJ2sLI", + "name": "ALIENS, LE RETOUR DE CAMERON : LA CRITIQUE CINÉ EN 3 MINUTES", + "size": 1080, + "type": "Featurette", + "url": "https://www.youtube.com/watch?v=X4xLFXJ2sLI" + }, + { + "site": "YouTube", + "key": "gaJFi3LiFOQ", + "name": "ALIENS Le retour (1986) Bande annonce française VF- HD", + "size": 1080, + "type": "Trailer", + "url": "https://www.youtube.com/watch?v=gaJFi3LiFOQ" + } + ], + "originalLanguage": "en", + "originalTitle": "Aliens", + "popularity": 210.35, + "productionCompanies": [ + { + "id": 396, + "name": "SLM Production Group", + "originCountry": "US", + "logoPath": "/5iNXO7nAiSsms7ur1Qy2UwuwcDP.png" + }, + { + "id": 25, + "name": "20th Century Fox", + "originCountry": "US", + "logoPath": "/qZCc1lty5FzX30aOCVRBLzaVmcp.png" + }, + { + "id": 401, + "name": "Brandywine Productions", + "originCountry": "US", + "logoPath": "/t7mM3DvQ9MwDT3YzMCBrkWpWiiz.png" + } + ], + "productionCountries": [ + { + "iso_3166_1": "US", + "name": "United States of America" + } + ], + "releaseDate": "1986-07-18", + "releases": { + "results": [ + { + "iso_3166_1": "AU", + "release_dates": [ + { + "certification": "M", + "descriptors": [], + "iso_639_1": "", + "note": "", + "release_date": "1986-11-06T00:00:00.000Z", + "type": 3 + } + ] + }, + { + "iso_3166_1": "BR", + "release_dates": [ + { + "certification": "14", + "descriptors": [], + "iso_639_1": "", + "note": "", + "release_date": "1986-12-18T00:00:00.000Z", + "type": 3 + } + ] + }, + { + "iso_3166_1": "CA", + "release_dates": [ + { + "certification": "14A", + "descriptors": [], + "iso_639_1": "", + "note": "", + "release_date": "1986-07-18T00:00:00.000Z", + "type": 3 + } + ] + }, + { + "iso_3166_1": "DE", + "release_dates": [ + { + "certification": "16", + "descriptors": [], + "iso_639_1": "", + "note": "", + "release_date": "1986-11-12T00:00:00.000Z", + "type": 3 + } + ] + }, + { + "iso_3166_1": "DK", + "release_dates": [ + { + "certification": "15", + "descriptors": [], + "iso_639_1": "", + "note": "", + "release_date": "1986-10-31T00:00:00.000Z", + "type": 3 + } + ] + }, + { + "iso_3166_1": "ES", + "release_dates": [ + { + "certification": "16", + "descriptors": [], + "iso_639_1": "es", + "note": "", + "release_date": "1986-10-29T00:00:00.000Z", + "type": 3 + } + ] + }, + { + "iso_3166_1": "FI", + "release_dates": [ + { + "certification": "K-18", + "descriptors": [], + "iso_639_1": "", + "note": "", + "release_date": "1986-10-31T00:00:00.000Z", + "type": 3 + } + ] + }, + { + "iso_3166_1": "FR", + "release_dates": [ + { + "certification": "12", + "descriptors": [], + "iso_639_1": "", + "note": "", + "release_date": "1986-10-08T00:00:00.000Z", + "type": 3 + }, + { + "certification": "", + "descriptors": [], + "iso_639_1": "", + "note": "Disney+", + "release_date": "2021-02-23T00:00:00.000Z", + "type": 4 + } + ] + }, + { + "iso_3166_1": "GB", + "release_dates": [ + { + "certification": "18", + "descriptors": [], + "iso_639_1": "", + "note": "", + "release_date": "1986-08-29T00:00:00.000Z", + "type": 3 + } + ] + }, + { + "iso_3166_1": "GR", + "release_dates": [ + { + "certification": "", + "descriptors": [], + "iso_639_1": "", + "note": "", + "release_date": "1987-01-22T00:00:00.000Z", + "type": 3 + } + ] + }, + { + "iso_3166_1": "HU", + "release_dates": [ + { + "certification": "18", + "descriptors": [], + "iso_639_1": "hu", + "note": "", + "release_date": "1988-04-07T00:00:00.000Z", + "type": 3 + }, + { + "certification": "18", + "descriptors": [], + "iso_639_1": "en", + "note": "Special Edition", + "release_date": "2015-01-15T00:00:00.000Z", + "type": 3 + }, + { + "certification": "18", + "descriptors": [], + "iso_639_1": "en", + "note": "4K Assembly Special Edition", + "release_date": "2023-03-30T00:00:00.000Z", + "type": 3 + } + ] + }, + { + "iso_3166_1": "IE", + "release_dates": [ + { + "certification": "18", + "descriptors": [], + "iso_639_1": "", + "note": "", + "release_date": "1986-09-19T00:00:00.000Z", + "type": 3 + } + ] + }, + { + "iso_3166_1": "IT", + "release_dates": [ + { + "certification": "T", + "descriptors": [], + "iso_639_1": "", + "note": "", + "release_date": "1986-09-24T00:00:00.000Z", + "type": 3 + }, + { + "certification": "", + "descriptors": [], + "iso_639_1": "", + "note": "Special edition", + "release_date": "1992-01-01T00:00:00.000Z", + "type": 5 + } + ] + }, + { + "iso_3166_1": "JP", + "release_dates": [ + { + "certification": "R18+", + "descriptors": [], + "iso_639_1": "", + "note": "", + "release_date": "1986-08-30T00:00:00.000Z", + "type": 3 + } + ] + }, + { + "iso_3166_1": "KR", + "release_dates": [ + { + "certification": "15", + "descriptors": [], + "iso_639_1": "", + "note": "", + "release_date": "1986-12-24T00:00:00.000Z", + "type": 3 + } + ] + }, + { + "iso_3166_1": "MX", + "release_dates": [ + { + "certification": "C", + "descriptors": [], + "iso_639_1": "es", + "note": "", + "release_date": "1986-12-25T00:00:00.000Z", + "type": 3 + } + ] + }, + { + "iso_3166_1": "NL", + "release_dates": [ + { + "certification": "12", + "descriptors": [], + "iso_639_1": "", + "note": "", + "release_date": "1986-10-09T00:00:00.000Z", + "type": 3 + }, + { + "certification": "12", + "descriptors": [], + "iso_639_1": "", + "note": "DVD", + "release_date": "2000-05-31T00:00:00.000Z", + "type": 5 + }, + { + "certification": "12", + "descriptors": [], + "iso_639_1": "", + "note": "Blu-ray", + "release_date": "2011-10-05T00:00:00.000Z", + "type": 5 + }, + { + "certification": "12", + "descriptors": [], + "iso_639_1": "", + "note": "V8", + "release_date": "2002-04-03T00:00:00.000Z", + "type": 6 + } + ] + }, + { + "iso_3166_1": "NO", + "release_dates": [ + { + "certification": "18", + "descriptors": [], + "iso_639_1": "", + "note": "", + "release_date": "1986-11-13T00:00:00.000Z", + "type": 3 + } + ] + }, + { + "iso_3166_1": "PL", + "release_dates": [ + { + "certification": "18", + "descriptors": [], + "iso_639_1": "", + "note": "", + "release_date": "1987-08-01T00:00:00.000Z", + "type": 3 + } + ] + }, + { + "iso_3166_1": "PR", + "release_dates": [ + { + "certification": "R", + "descriptors": [], + "iso_639_1": "", + "note": "4K remaster", + "release_date": "2023-12-12T00:00:00.000Z", + "type": 4 + }, + { + "certification": "R", + "descriptors": [], + "iso_639_1": "", + "note": "4K Ultra HD", + "release_date": "2024-03-12T00:00:00.000Z", + "type": 5 + } + ] + }, + { + "iso_3166_1": "PT", + "release_dates": [ + { + "certification": "M/18", + "descriptors": [], + "iso_639_1": "", + "note": "", + "release_date": "1986-12-18T00:00:00.000Z", + "type": 3 + } + ] + }, + { + "iso_3166_1": "RO", + "release_dates": [ + { + "certification": "15", + "descriptors": [], + "iso_639_1": "", + "note": "", + "release_date": "1986-07-18T00:00:00.000Z", + "type": 1 + } + ] + }, + { + "iso_3166_1": "SE", + "release_dates": [ + { + "certification": "15", + "descriptors": [], + "iso_639_1": "", + "note": "", + "release_date": "1986-10-03T00:00:00.000Z", + "type": 3 + }, + { + "certification": "15", + "descriptors": [], + "iso_639_1": "", + "note": "DVD", + "release_date": "2001-11-28T00:00:00.000Z", + "type": 5 + }, + { + "certification": "15", + "descriptors": [], + "iso_639_1": "", + "note": "Blu-ray", + "release_date": "2010-10-27T00:00:00.000Z", + "type": 5 + } + ] + }, + { + "iso_3166_1": "US", + "release_dates": [ + { + "certification": "R", + "descriptors": [], + "iso_639_1": "", + "note": "Westwood, California", + "release_date": "1986-07-14T00:00:00.000Z", + "type": 1 + }, + { + "certification": "R", + "descriptors": [], + "iso_639_1": "", + "note": "", + "release_date": "1986-07-18T00:00:00.000Z", + "type": 3 + }, + { + "certification": "R", + "descriptors": [], + "iso_639_1": "", + "note": "4K remaster", + "release_date": "2023-12-12T00:00:00.000Z", + "type": 4 + }, + { + "certification": "R", + "descriptors": [], + "iso_639_1": "", + "note": "4K Ultra HD", + "release_date": "2024-03-12T00:00:00.000Z", + "type": 5 + } + ] + }, + { + "iso_3166_1": "XC", + "release_dates": [ + { + "certification": "", + "descriptors": [], + "iso_639_1": "", + "note": "", + "release_date": "1991-06-06T00:00:00.000Z", + "type": 3 + } + ] + } + ] + }, + "revenue": 183316455, + "spokenLanguages": [ + { + "english_name": "English", + "iso_639_1": "en", + "name": "English" + } + ], + "status": "Released", + "title": "Aliens, le retour", + "video": false, + "voteAverage": 7.943, + "voteCount": 8555, + "backdropPath": "/jMBpJFRtrtIXymer93XLavPwI3P.jpg", + "homepage": "", + "imdbId": "tt0090605", + "overview": "Après 57 ans de dérive dans l’espace, Ellen Ripley est secourue par la corporation Weyland‐Yutani. Malgré son rapport concernant l’incident survenu sur le Nostromo, elle n’est pas prise au sérieux par les militaires quant à la présence de xénomorphes sur la planète LV‐426 où se posa son équipage… planète où plusieurs familles de colons ont été envoyées en mission de « terraformage ». Après la disparition de ces derniers, Ripley décide d’accompagner une escouade de marines dans leur mission de sauvetage… et d’affronter à nouveau la Bête.", + "posterPath": "/uafMg29wVV4XvxPS59s8nBBbP4i.jpg", + "runtime": 137, + "tagline": "Cette fois c'est la guerre.", + "credits": { + "cast": [ + { + "castId": 21, + "character": "Ripley", + "creditId": "52fe4269c3a36847f801c9bf", + "id": 10205, + "name": "Sigourney Weaver", + "order": 0, + "gender": 1, + "profilePath": "/wTSnfktNBLd6kwQxgvkqYw6vEon.jpg" + }, + { + "castId": 22, + "character": "Newt", + "creditId": "52fe4269c3a36847f801c9c3", + "id": 10206, + "name": "Carrie Henn", + "order": 1, + "gender": 1, + "profilePath": "/iNaYp4liKvKsEZp5g5xjd94OFCz.jpg" + }, + { + "castId": 23, + "character": "Corporal Hicks", + "creditId": "52fe4269c3a36847f801c9c7", + "id": 2712, + "name": "Michael Biehn", + "order": 2, + "gender": 2, + "profilePath": "/9oFLsADWQm2TvU8XzLIzBbjdMkU.jpg" + }, + { + "castId": 25, + "character": "Burke", + "creditId": "52fe4269c3a36847f801c9cf", + "id": 781, + "name": "Paul Reiser", + "order": 3, + "gender": 2, + "profilePath": "/v3e5TClep4ugMdU4qSRiKbvLr3B.jpg" + }, + { + "castId": 24, + "character": "Bishop", + "creditId": "52fe4269c3a36847f801c9cb", + "id": 2714, + "name": "Lance Henriksen", + "order": 4, + "gender": 2, + "profilePath": "/5F8cHWGlo2NIiswSn5ymL5ukgxG.jpg" + }, + { + "castId": 26, + "character": "Private Hudson", + "creditId": "52fe4269c3a36847f801c9d3", + "id": 2053, + "name": "Bill Paxton", + "order": 5, + "gender": 2, + "profilePath": "/aLdNe6mt1cSi2zWlUYRregzfis5.jpg" + }, + { + "castId": 27, + "character": "Lieutenant Gorman", + "creditId": "52fe4269c3a36847f801c9d7", + "id": 10207, + "name": "William Hope", + "order": 6, + "gender": 2, + "profilePath": "/blKMEeCftWVBPI2hqvzycmdE9nS.jpg" + }, + { + "castId": 28, + "character": "Private Vasquez", + "creditId": "52fe4269c3a36847f801c9db", + "id": 3981, + "name": "Jenette Goldstein", + "order": 7, + "gender": 1, + "profilePath": "/4YZVLLWEFtxPLaOAlndfzxH1WG1.jpg" + }, + { + "castId": 29, + "character": "Sergeant Apone", + "creditId": "52fe4269c3a36847f801c9df", + "id": 10208, + "name": "Al Matthews", + "order": 8, + "gender": 2, + "profilePath": "/ugW320vaF9XdIaowEDIwcF3CXyU.jpg" + }, + { + "castId": 30, + "character": "Private Drake", + "creditId": "52fe4269c3a36847f801c9e3", + "id": 6576, + "name": "Mark Rolston", + "order": 9, + "gender": 2, + "profilePath": "/hcrNRIptYMRXgkJ9k76BlQu6DQp.jpg" + }, + { + "castId": 34, + "character": "Private Frost", + "creditId": "52fe4269c3a36847f801c9f3", + "id": 10212, + "name": "Ricco Ross", + "order": 10, + "gender": 2, + "profilePath": "/uXnxQldNApoiTJMecHA5xbi222S.jpg" + }, + { + "castId": 31, + "character": "Corporal Ferro", + "creditId": "52fe4269c3a36847f801c9e7", + "id": 10209, + "name": "Colette Hiller", + "order": 11, + "gender": 1, + "profilePath": "/oOF8jmi10ZuwkUDOobGmYGfjOIt.jpg" + }, + { + "castId": 32, + "character": "Private Spunkmeyer", + "creditId": "52fe4269c3a36847f801c9eb", + "id": 10210, + "name": "Daniel Kash", + "order": 12, + "gender": 2, + "profilePath": "/v8xa3aFbdk0t7RXpXiLlXI7QepH.jpg" + }, + { + "castId": 33, + "character": "Corporal Dietrich", + "creditId": "52fe4269c3a36847f801c9ef", + "id": 10211, + "name": "Cynthia Dale Scott", + "order": 13, + "gender": 1, + "profilePath": "/320o5vqAhKMt2U7SDNcU4pPiXJ3.jpg" + }, + { + "castId": 35, + "character": "Private Crowe", + "creditId": "52fe4269c3a36847f801c9f7", + "id": 10213, + "name": "Tip Tipping", + "order": 14, + "gender": 2, + "profilePath": "/vf6pIofI1U77O1GGk2I8yLEWgXL.jpg" + }, + { + "castId": 57, + "character": "Private Wierzbowski", + "creditId": "578e2ac2c3a3686efa00a946", + "id": 1340211, + "name": "Trevor Steedman", + "order": 15, + "gender": 2, + "profilePath": null + }, + { + "castId": 59, + "character": "Van Leuwen", + "creditId": "578e2d1e925141188a003ce3", + "id": 748, + "name": "Paul Maxwell", + "order": 16, + "gender": 2, + "profilePath": null + }, + { + "castId": 58, + "character": "ECA Rep", + "creditId": "578e2c099251415407009431", + "id": 1653085, + "name": "Valerie Colgan", + "order": 17, + "gender": 1, + "profilePath": "/qVrWdI8cpkAnB7j8t6t4Th6I64Y.jpg" + }, + { + "castId": 60, + "character": "Insurance Man", + "creditId": "578e2d56c3a3680d34008f3c", + "id": 84890, + "name": "Alan Polonsky", + "order": 18, + "gender": 2, + "profilePath": "/mX7V29fZjWVrP0cWHBNM8uiklrF.jpg" + }, + { + "castId": 61, + "character": "Med Tech", + "creditId": "578e2ec7925141180b003a1c", + "id": 190455, + "name": "Alibe Parsons", + "order": 19, + "gender": 1, + "profilePath": "/flcGz17vn1sEo6UzwSEroDw5XPE.jpg" + }, + { + "castId": 62, + "character": "Doctor", + "creditId": "578e3056c3a3685acb0125b5", + "id": 1179692, + "name": "Blain Fairman", + "order": 20, + "gender": 2, + "profilePath": null + }, + { + "castId": 63, + "character": "Cocooned Woman", + "creditId": "578e3193925141086400e3e6", + "id": 96991, + "name": "Barbara Coles", + "order": 21, + "gender": 1, + "profilePath": null + }, + { + "castId": 67, + "character": "Alien Warrior", + "creditId": "591a2f04c3a368423c060847", + "id": 1817689, + "name": "Carl Toop", + "order": 22, + "gender": 0, + "profilePath": null + }, + { + "castId": 114, + "character": "Power Loader Operator", + "creditId": "5f92502fa9b9a40039c752a0", + "id": 1494026, + "name": "John Lees", + "order": 23, + "gender": 2, + "profilePath": null + }, + { + "castId": 116, + "character": "Newt's Mother", + "creditId": "5f9250d6d9f4a60037daf415", + "id": 84893, + "name": "Holly De Jong", + "order": 24, + "gender": 1, + "profilePath": "/3UtwsSuOaDueUg5iWOxX8KJw58p.jpg" + }, + { + "castId": 128, + "character": "Newt's Brother", + "creditId": "664decc2595c5c18e113f163", + "id": 3032600, + "name": "Christopher Henn", + "order": 25, + "gender": 0, + "profilePath": null + }, + { + "castId": 65, + "character": "Newt's Father", + "creditId": "578e3585c3a36817480041c2", + "id": 141014, + "name": "Jay Benedict", + "order": 26, + "gender": 2, + "profilePath": "/egmUxHP7CUIK0GhdEVhjkS1fa8e.jpg" + }, + { + "castId": 115, + "character": "Lydecker", + "creditId": "5f9250aba9b9a40038c7ebe1", + "id": 29616, + "name": "William Armstrong", + "order": 27, + "gender": 2, + "profilePath": "/lW4dqrVuKKi3CK094JBJedSH6Qz.jpg" + }, + { + "castId": 79, + "character": "Simpson", + "creditId": "5e5594b8a76ac50013a9c9c0", + "id": 33403, + "name": "Mac McDonald", + "order": 28, + "gender": 2, + "profilePath": "/gejehuptBzsJlPiWKU8B9TTrc7r.jpg" + }, + { + "castId": 64, + "character": "Alien Warrior (uncredited)", + "creditId": "578e342b9251415407009810", + "id": 1077325, + "name": "Eddie Powell", + "order": 29, + "gender": 2, + "profilePath": "/rdrJbQxIokXxiBHCJuAsqTKFbXH.jpg" + }, + { + "castId": 117, + "character": "Worker in Corridor (Special Edition) (uncredited)", + "creditId": "5f9250de4581990033a9fc73", + "id": 2462366, + "name": "Jill Goldston", + "order": 30, + "gender": 1, + "profilePath": "/9DcoU2J3OvunNAUTj94dCI6bYru.jpg" + }, + { + "castId": 118, + "character": "Amanda Ripley-Mclaren (uncredited)", + "creditId": "5f9250f8383df20034b62178", + "id": 3678, + "name": "Elizabeth Inglis", + "order": 31, + "gender": 1, + "profilePath": "/geKNfejtiMClQxX5FmPUKk954xe.jpg" + }, + { + "castId": 119, + "character": "Salvage Team Leader (uncredited)", + "creditId": "5f92510cc1ffbd0039a40aff", + "id": 96895, + "name": "Stuart Milligan", + "order": 32, + "gender": 2, + "profilePath": "/gFZCxMayTR1xdS2m16QNbecY3I4.jpg" + }, + { + "castId": 120, + "character": "Salvage Team Leader (voice) (uncredited)", + "creditId": "5f925113383df20034b62215", + "id": 74762, + "name": "Bob Sherman", + "order": 33, + "gender": 2, + "profilePath": null + }, + { + "castId": 121, + "character": "Alien Warrior (uncredited)", + "creditId": "5f92513c383df20034b62426", + "id": 1902347, + "name": "Chris Webb", + "order": 34, + "gender": 2, + "profilePath": "/ynXbiAuNnQ7VrZOX0P1mkdG2l9E.jpg" + }, + { + "castId": 122, + "character": "Salvager (uncredited)", + "creditId": "5f925143ca83540036b6c201", + "id": 109870, + "name": "Tom Woodruff Jr.", + "order": 35, + "gender": 2, + "profilePath": "/o5MW7KiROaA5uf9zzJmpHJfUjh3.jpg" + }, + { + "castId": 124, + "character": "Alien (uncredited)", + "creditId": "62f0095f813cb6007b33bea0", + "id": 958540, + "name": "Alec Gillis", + "order": 36, + "gender": 2, + "profilePath": "/3pglJqJepYKuE8B9aIAd4Z3qX0y.jpg" + } + ], + "crew": [ + { + "creditId": "52fe4269c3a36847f801c94f", + "department": "Directing", + "id": 2710, + "job": "Director", + "name": "James Cameron", + "gender": 2, + "profilePath": "/5tXGxsQyp05biKpq99NKyGRuFHX.jpg" + }, + { + "creditId": "52fe4269c3a36847f801c955", + "department": "Production", + "id": 869, + "job": "Producer", + "name": "Gale Anne Hurd", + "gender": 1, + "profilePath": "/zQtqYXqna1DyIMX3TrY0UytNgoO.jpg" + }, + { + "creditId": "52fe4269c3a36847f801c95b", + "department": "Sound", + "id": 1729, + "job": "Original Music Composer", + "name": "James Horner", + "gender": 2, + "profilePath": "/g2C3Fb4fHCU2gnqUcwTQCv5EZc4.jpg" + }, + { + "creditId": "52fe4269c3a36847f801c961", + "department": "Camera", + "id": 7783, + "job": "Director of Photography", + "name": "Adrian Biddle", + "gender": 2, + "profilePath": "/9GpjIB232uNdLA6bUrrlKvau25E.jpg" + }, + { + "creditId": "52fe4269c3a36847f801c967", + "department": "Editing", + "id": 244, + "job": "Editor", + "name": "Ray Lovejoy", + "gender": 2, + "profilePath": null + }, + { + "creditId": "52fe4269c3a36847f801c96d", + "department": "Art", + "id": 8524, + "job": "Production Design", + "name": "Peter Lamont", + "gender": 2, + "profilePath": "/fYRYLO1GTvXULvwR2EfyHyFH1Ve.jpg" + }, + { + "creditId": "52fe4269c3a36847f801c973", + "department": "Art", + "id": 7204, + "job": "Art Direction", + "name": "Ken Court", + "gender": 2, + "profilePath": null + }, + { + "creditId": "52fe4269c3a36847f801c979", + "department": "Art", + "id": 10183, + "job": "Art Direction", + "name": "Bert Davey", + "gender": 0, + "profilePath": null + }, + { + "creditId": "52fe4269c3a36847f801c97f", + "department": "Art", + "id": 10197, + "job": "Art Direction", + "name": "Fred Hole", + "gender": 2, + "profilePath": null + }, + { + "creditId": "52fe4269c3a36847f801c985", + "department": "Art", + "id": 8380, + "job": "Art Direction", + "name": "Michael Lamont", + "gender": 2, + "profilePath": null + }, + { + "creditId": "52fe4269c3a36847f801c98b", + "department": "Art", + "id": 945, + "job": "Set Decoration", + "name": "Crispian Sallis", + "gender": 2, + "profilePath": "/gv083KVBiGKPWkmN6b0CcivESrA.jpg" + }, + { + "creditId": "52fe4269c3a36847f801c991", + "department": "Costume & Make-Up", + "id": 10198, + "job": "Costume Design", + "name": "Emma Porteous", + "gender": 1, + "profilePath": null + }, + { + "creditId": "52fe4269c3a36847f801c9a9", + "department": "Sound", + "id": 10201, + "job": "Music Editor", + "name": "Michael Clifford", + "gender": 2, + "profilePath": null + }, + { + "creditId": "52fe4269c3a36847f801c9af", + "department": "Crew", + "id": 10202, + "job": "Special Effects", + "name": "Norman Baillie", + "gender": 0, + "profilePath": null + }, + { + "creditId": "52fe4269c3a36847f801c9b5", + "department": "Crew", + "id": 10203, + "job": "Stunt Coordinator", + "name": "Paul Weston", + "gender": 2, + "profilePath": "/9eNrtUeWeDudwB3OzSQBbNXQWlP.jpg" + }, + { + "creditId": "52fe4269c3a36847f801c9bb", + "department": "Crew", + "id": 10204, + "job": "Stunts", + "name": "Elanor Bertram", + "gender": 0, + "profilePath": null + }, + { + "creditId": "52fe4269c3a36847f801c9fd", + "department": "Writing", + "id": 2710, + "job": "Screenplay", + "name": "James Cameron", + "gender": 2, + "profilePath": "/5tXGxsQyp05biKpq99NKyGRuFHX.jpg" + }, + { + "creditId": "52fe4269c3a36847f801ca03", + "department": "Crew", + "id": 9402, + "job": "Special Effects", + "name": "Brian Johnson", + "gender": 0, + "profilePath": null + }, + { + "creditId": "570a7e37c3a3680b77004719", + "department": "Art", + "id": 10200, + "job": "Supervising Art Director", + "name": "Terry Ackland-Snow", + "gender": 2, + "profilePath": null + }, + { + "creditId": "53957df00e0a266dbf00626c", + "department": "Costume & Make-Up", + "id": 29061, + "job": "Costume Supervisor", + "name": "Tiny Nicholls", + "gender": 0, + "profilePath": null + }, + { + "creditId": "53957e170e0a266db70063f8", + "department": "Production", + "id": 597, + "job": "Casting", + "name": "Jane Feinberg", + "gender": 1, + "profilePath": null + }, + { + "creditId": "53957e290e0a266dbf006271", + "department": "Production", + "id": 598, + "job": "Casting", + "name": "Mike Fenton", + "gender": 2, + "profilePath": "/oIKCBb2w47iZps9neDo2GY29g1y.jpg" + }, + { + "creditId": "53957e390e0a266dbb006555", + "department": "Production", + "id": 668, + "job": "Casting", + "name": "Mary Selway", + "gender": 1, + "profilePath": "/9dP8WP2KF9TERPLQkaXDwzi3Tjc.jpg" + }, + { + "creditId": "53957e4b0e0a266dbf006274", + "department": "Production", + "id": 752, + "job": "Casting", + "name": "Judy Taylor", + "gender": 1, + "profilePath": null + }, + { + "creditId": "548d94ebc3a3684ea1003cf5", + "department": "Camera", + "id": 1399033, + "job": "Camera Operator", + "name": "Shaun O'Dell", + "gender": 2, + "profilePath": null + }, + { + "creditId": "548d934a92514122f2003fe5", + "department": "Sound", + "id": 20110, + "job": "Supervising Sound Editor", + "name": "Don Sharpe", + "gender": 0, + "profilePath": null + }, + { + "creditId": "548d945d92514122ff003dcb", + "department": "Visual Effects", + "id": 1399031, + "job": "Visual Effects Supervisor", + "name": "Richard Hewitt", + "gender": 0, + "profilePath": null + }, + { + "creditId": "548d9490c3a3681d4b002046", + "department": "Visual Effects", + "id": 1399032, + "job": "Visual Effects Supervisor", + "name": "Dennis Skotak", + "gender": 0, + "profilePath": null + }, + { + "creditId": "548d94a6c3a36820c50044ad", + "department": "Visual Effects", + "id": 102344, + "job": "Visual Effects Supervisor", + "name": "Robert Skotak", + "gender": 0, + "profilePath": null + }, + { + "creditId": "548d94fdc3a36820be00393f", + "department": "Camera", + "id": 40765, + "job": "Camera Operator", + "name": "David Worley", + "gender": 0, + "profilePath": null + }, + { + "creditId": "548d951292514122ff003dde", + "department": "Camera", + "id": 1378839, + "job": "Still Photographer", + "name": "Bob Penn", + "gender": 0, + "profilePath": null + }, + { + "creditId": "548d9526925141031b0006bb", + "department": "Lighting", + "id": 1399035, + "job": "Gaffer", + "name": "Jack Thetford", + "gender": 0, + "profilePath": null + }, + { + "creditId": "59da2026c3a36861ec0405c1", + "department": "Crew", + "id": 1902347, + "job": "Stunts", + "name": "Chris Webb", + "gender": 2, + "profilePath": "/ynXbiAuNnQ7VrZOX0P1mkdG2l9E.jpg" + }, + { + "creditId": "5a5a63cd0e0a267fe00113f1", + "department": "Sound", + "id": 296, + "job": "Music Editor", + "name": "Robin Clarke", + "gender": 2, + "profilePath": null + }, + { + "creditId": "5c9129fc0e0a2663c751edb6", + "department": "Writing", + "id": 5046, + "job": "Characters", + "name": "Ronald Shusett", + "gender": 2, + "profilePath": "/fNfdVzGnrp1PKfdhCdUWOvp36J9.jpg" + }, + { + "creditId": "5c912a310e0a2612ef5b6f56", + "department": "Writing", + "id": 5045, + "job": "Characters", + "name": "Dan O'Bannon", + "gender": 2, + "profilePath": "/k52NrI4p37oboVIMqxLA05Qr4rl.jpg" + }, + { + "creditId": "5c912a3b0e0a2612ef5b6f6e", + "department": "Writing", + "id": 915, + "job": "Story", + "name": "David Giler", + "gender": 2, + "profilePath": "/mKsyy0SS0YNWBgDQp2Hse3i9g2h.jpg" + }, + { + "creditId": "5c912a440e0a2612ed5b25f2", + "department": "Writing", + "id": 1723, + "job": "Story", + "name": "Walter Hill", + "gender": 2, + "profilePath": "/3CiICrmhaYnirmOMHEaFXYTbsnT.jpg" + }, + { + "creditId": "5c912a4e0e0a2612f55b63bd", + "department": "Writing", + "id": 2710, + "job": "Story", + "name": "James Cameron", + "gender": 2, + "profilePath": "/5tXGxsQyp05biKpq99NKyGRuFHX.jpg" + }, + { + "creditId": "5c912adcc3a3686119512fc7", + "department": "Production", + "id": 1723, + "job": "Executive Producer", + "name": "Walter Hill", + "gender": 2, + "profilePath": "/3CiICrmhaYnirmOMHEaFXYTbsnT.jpg" + }, + { + "creditId": "5c912ae50e0a2612ef5b705a", + "department": "Production", + "id": 915, + "job": "Executive Producer", + "name": "David Giler", + "gender": 2, + "profilePath": "/mKsyy0SS0YNWBgDQp2Hse3i9g2h.jpg" + }, + { + "creditId": "5c912aeec3a3681c954e0312", + "department": "Production", + "id": 5053, + "job": "Executive Producer", + "name": "Gordon Carroll", + "gender": 2, + "profilePath": null + }, + { + "creditId": "5dbaa62612970c001838223c", + "department": "Art", + "id": 1825080, + "job": "Construction Manager", + "name": "Vic Simpson", + "gender": 2, + "profilePath": null + }, + { + "creditId": "5e559606f48b34001375eabd", + "department": "Costume & Make-Up", + "id": 1838313, + "job": "Key Hair Stylist", + "name": "Elaine Bowerbank", + "gender": 0, + "profilePath": null + }, + { + "creditId": "5e559631f48b34001775bb42", + "department": "Production", + "id": 2550220, + "job": "Unit Production Manager", + "name": "Mo Coppitters", + "gender": 0, + "profilePath": null + }, + { + "creditId": "5e5596641e9225001b1f7bb9", + "department": "Production", + "id": 958768, + "job": "Production Supervisor", + "name": "Hugh Harlow", + "gender": 2, + "profilePath": null + }, + { + "creditId": "5e559697efea7a0010ff39dd", + "department": "Production", + "id": 1580875, + "job": "Unit Production Manager", + "name": "Gil Whelan", + "gender": 0, + "profilePath": null + }, + { + "creditId": "5e5596dc35811d00175d753b", + "department": "Directing", + "id": 1513635, + "job": "First Assistant Director", + "name": "Derek Cracknell", + "gender": 2, + "profilePath": "/6rrEbuX2hc5SrsKQICFiKKTWoAE.jpg" + }, + { + "creditId": "5e559764848eb90014f366b6", + "department": "Directing", + "id": 2238043, + "job": "Second Assistant Director", + "name": "Melvin Lind", + "gender": 0, + "profilePath": null + }, + { + "creditId": "5e55980515959f0013a91328", + "department": "Directing", + "id": 60261, + "job": "Second Unit Director", + "name": "Stan Winston", + "gender": 2, + "profilePath": "/k1L7O1pIQ1fveNnKBcLNICL2cIm.jpg" + }, + { + "creditId": "5e55987935811d00195d0eca", + "department": "Art", + "id": 25195, + "job": "Conceptual Design", + "name": "Ron Cobb", + "gender": 2, + "profilePath": "/eR3uyIVpW3lzANchDdS5Nde9hu.jpg" + }, + { + "creditId": "5e5598b7a6d931001259bd08", + "department": "Art", + "id": 12867, + "job": "Concept Artist", + "name": "Syd Mead", + "gender": 2, + "profilePath": "/xLp9zBAOwrvgQQ55goeIpBJuNyE.jpg" + }, + { + "creditId": "5e55996a1e922500181f8202", + "department": "Sound", + "id": 2010309, + "job": "Foley Editor", + "name": "Rocky Phelan", + "gender": 0, + "profilePath": null + }, + { + "creditId": "5e5599eaf48b34001375f23e", + "department": "Crew", + "id": 142325, + "job": "Stunts", + "name": "Simon Crane", + "gender": 2, + "profilePath": "/8wEeMwpBEcS43XC8o2s2gad9cjy.jpg" + }, + { + "creditId": "5e559a131e9225001d1f8501", + "department": "Crew", + "id": 2446820, + "job": "Stunts", + "name": "Sue Crosland", + "gender": 1, + "profilePath": null + }, + { + "creditId": "5e559a301e922500181f82db", + "department": "Crew", + "id": 1388877, + "job": "Stunts", + "name": "Steve Dent", + "gender": 2, + "profilePath": "/KqTlsFdksLraqd4HBvGUemwfhe.jpg" + }, + { + "creditId": "5e559a8fa76ac50016a98747", + "department": "Crew", + "id": 1212168, + "job": "Stunts", + "name": "Stuart Fell", + "gender": 2, + "profilePath": null + }, + { + "creditId": "5e559aa31e9225001d1f85d7", + "department": "Crew", + "id": 2550253, + "job": "Stunt Double", + "name": "Louise Head", + "gender": 0, + "profilePath": null + }, + { + "creditId": "5e559ac4a93d2500134f7fb6", + "department": "Crew", + "id": 1534130, + "job": "Stunts", + "name": "Jazzer Jeyes", + "gender": 2, + "profilePath": null + }, + { + "creditId": "5e559af4efea7a0018ff517f", + "department": "Crew", + "id": 1419427, + "job": "Stunts", + "name": "Sean McCabe", + "gender": 2, + "profilePath": null + }, + { + "creditId": "5e559b971e922500181f85a5", + "department": "Crew", + "id": 1077325, + "job": "Stunts", + "name": "Eddie Powell", + "gender": 2, + "profilePath": "/rdrJbQxIokXxiBHCJuAsqTKFbXH.jpg" + }, + { + "creditId": "5e559bb61e922500161f9204", + "department": "Crew", + "id": 5531, + "job": "Stunt Double", + "name": "Kiran Shah", + "gender": 2, + "profilePath": "/cBE8YAZeCMVUu4eAxr6PNUmNjCh.jpg" + }, + { + "creditId": "5e559be4f48b34001575b8c1", + "department": "Crew", + "id": 36903, + "job": "Stunts", + "name": "Stuart St. Paul", + "gender": 2, + "profilePath": null + }, + { + "creditId": "5e559c01848eb90014f36b9d", + "department": "Crew", + "id": 1402756, + "job": "Stunts", + "name": "Malcolm Weaver", + "gender": 0, + "profilePath": null + }, + { + "creditId": "5e559c251e922500181f8638", + "department": "Crew", + "id": 24278, + "job": "Stunts", + "name": "Bill Weston", + "gender": 2, + "profilePath": "/mQtWjSvfDlgI42NrE5E3nfICGbE.jpg" + }, + { + "creditId": "5e559ca1efea7a0013ff3380", + "department": "Crew", + "id": 1456409, + "job": "Stunts", + "name": "Jason White", + "gender": 2, + "profilePath": null + }, + { + "creditId": "5e559ccaa93d2500154f7fbd", + "department": "Crew", + "id": 1205484, + "job": "Stunt Double", + "name": "Clive Curtis", + "gender": 2, + "profilePath": null + }, + { + "creditId": "5e559d341e922500161f92ab", + "department": "Editing", + "id": 25823, + "job": "Associate Editor", + "name": "Peter Boita", + "gender": 2, + "profilePath": null + }, + { + "creditId": "5e559d78efea7a0018ff5686", + "department": "Editing", + "id": 2502976, + "job": "First Assistant Editor", + "name": "Phil Sanderson", + "gender": 2, + "profilePath": null + }, + { + "creditId": "5e559dbc35811d00135b8c23", + "department": "Sound", + "id": 1057871, + "job": "Orchestrator", + "name": "Greig McRitchie", + "gender": 0, + "profilePath": null + }, + { + "creditId": "5e559e3f1e922500161f9373", + "department": "Directing", + "id": 1394104, + "job": "Script Supervisor", + "name": "Diana Dill", + "gender": 1, + "profilePath": null + }, + { + "creditId": "5e559eb235811d00175d7be6", + "department": "Crew", + "id": 1406988, + "job": "Unit Publicist", + "name": "Geoff Freeman", + "gender": 2, + "profilePath": null + }, + { + "creditId": "5e559f2335811d00195d17b2", + "department": "Visual Effects", + "id": 9136, + "job": "Creature Design", + "name": "H. R. Giger", + "gender": 2, + "profilePath": "/1PzfrXpbtaLnyRJpoXUKFl2gwts.jpg" + }, + { + "creditId": "5e559f46a6d931001659bb42", + "department": "Crew", + "id": 72471, + "job": "Production Controller", + "name": "Paul Tucker", + "gender": 2, + "profilePath": null + }, + { + "creditId": "5e559f7ca76ac50016a98bc5", + "department": "Production", + "id": 2550262, + "job": "Production Coordinator", + "name": "Joyce Turner", + "gender": 0, + "profilePath": null + }, + { + "creditId": "5f033f310f36550039d7698d", + "department": "Costume & Make-Up", + "id": 10199, + "job": "Makeup Supervisor", + "name": "Peter Robb-King", + "gender": 2, + "profilePath": "/uyJpe7UQoFhpqChXpHwknqQ88X7.jpg" + }, + { + "creditId": "5f0340a8dd258900399e525c", + "department": "Crew", + "id": 7790, + "job": "Visual Effects Art Director", + "name": "Peter Russell", + "gender": 0, + "profilePath": null + }, + { + "creditId": "60f7ba5f3103250046ccf686", + "department": "Editing", + "id": 2010304, + "job": "Assistant Editor", + "name": "Simon Harris", + "gender": 2, + "profilePath": null + }, + { + "creditId": "63732af45a469000919e36f9", + "department": "Sound", + "id": 41840, + "job": "Dialogue Editor", + "name": "Archie Ludski", + "gender": 2, + "profilePath": null + }, + { + "creditId": "63732b168fdda900b4514391", + "department": "Sound", + "id": 1399030, + "job": "Sound Recordist", + "name": "Roy Charman", + "gender": 0, + "profilePath": null + }, + { + "creditId": "63732b308fdda900aadde097", + "department": "Art", + "id": 1399025, + "job": "Property Master", + "name": "Bert Hearn", + "gender": 0, + "profilePath": null + } + ] + }, + "collection": { + "id": 8091, + "name": "Alien - Saga", + "posterPath": "/ygHKpMEcbarcC7YcaKekhzz3cUl.jpg", + "backdropPath": "/6X42JnSMdo3dPAswOHUuvebdTq7.jpg" + }, + "externalIds": { + "facebookId": "alienanthology", + "imdbId": "tt0090605", + "instagramId": "alienanthology", + "twitterId": "alienanthology" + }, + "mediaInfo": { + "downloadStatus": [], + "downloadStatus4k": [], + "id": 549, + "mediaType": "movie", + "tmdbId": 679, + "tvdbId": null, + "imdbId": null, + "status": 5, + "status4k": 1, + "createdAt": "2023-12-16T12:48:28.000Z", + "updatedAt": "2023-12-23T18:05:00.000Z", + "lastSeasonChange": "2023-12-16T12:48:28.000Z", + "mediaAddedAt": "2023-12-23T18:03:23.000Z", + "serviceId": 0, + "serviceId4k": null, + "externalServiceId": 111, + "externalServiceId4k": null, + "externalServiceSlug": "679", + "externalServiceSlug4k": null, + "ratingKey": "5667", + "ratingKey4k": null, + "requests": [ + { + "id": 130, + "status": 2, + "createdAt": "2023-12-16T12:48:28.000Z", + "updatedAt": "2023-12-16T12:48:28.000Z", + "type": "movie", + "is4k": false, + "serverId": 0, + "profileId": 8, + "rootFolder": "/xxx", + "languageProfileId": null, + "tags": [], + "isAutoRequest": false, + "media": { + "downloadStatus": [], + "downloadStatus4k": [], + "id": 549, + "mediaType": "movie", + "tmdbId": 679, + "tvdbId": null, + "imdbId": null, + "status": 5, + "status4k": 1, + "createdAt": "2023-12-16T12:48:28.000Z", + "updatedAt": "2023-12-23T18:05:00.000Z", + "lastSeasonChange": "2023-12-16T12:48:28.000Z", + "mediaAddedAt": "2023-12-23T18:03:23.000Z", + "serviceId": 0, + "serviceId4k": null, + "externalServiceId": 111, + "externalServiceId4k": null, + "externalServiceSlug": "679", + "externalServiceSlug4k": null, + "ratingKey": "5667", + "ratingKey4k": null, + "plexUrl": "xxx", + "iOSPlexUrl": "xxx", + "serviceUrl": "xxx" + }, + "requestedBy": { + "permissions": 2, + "id": 1, + "email": "xxx", + "plexUsername": "xxx", + "username": "xxx", + "recoveryLinkExpirationDate": null, + "userType": 1, + "plexId": 8862231, + "avatar": "xxx", + "movieQuotaLimit": null, + "movieQuotaDays": null, + "tvQuotaLimit": null, + "tvQuotaDays": null, + "createdAt": "2023-07-08T19:56:30.000Z", + "updatedAt": "2024-08-16T23:56:27.000Z", + "requestCount": 245, + "displayName": "xxx" + }, + "modifiedBy": { + "permissions": 2, + "id": 1, + "email": "xxx", + "plexUsername": "xxx", + "username": "xxx", + "recoveryLinkExpirationDate": null, + "userType": 1, + "plexId": 8862231, + "avatar": "xxx", + "movieQuotaLimit": null, + "movieQuotaDays": null, + "tvQuotaLimit": null, + "tvQuotaDays": null, + "createdAt": "2023-07-08T19:56:30.000Z", + "updatedAt": "2024-08-16T23:56:27.000Z", + "requestCount": 2, + "displayName": "xxx" + }, + "seasons": [], + "seasonCount": 0 + } + ], + "issues": [], + "seasons": [], + "plexUrl": "xxx", + "iOSPlexUrl": "xxx", + "serviceUrl": "xxx" + }, + "watchProviders": [ + { + "iso_3166_1": "AD", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=AD", + "buy": [], + "flatrate": [ + { + "displayPriority": 7, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "AE", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=AE", + "buy": [ + { + "displayPriority": 1, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 3, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + } + ], + "flatrate": [] + }, + { + "iso_3166_1": "AG", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=AG", + "buy": [], + "flatrate": [ + { + "displayPriority": 13, + "logoPath": "/fksCUZ9QDWZMUwL2LgMtLckROUN.jpg", + "id": 1899, + "name": "Max" + } + ] + }, + { + "iso_3166_1": "AL", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=AL", + "buy": [ + { + "displayPriority": 8, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + } + ], + "flatrate": [ + { + "displayPriority": 7, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "AR", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=AR", + "buy": [ + { + "displayPriority": 4, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + } + ], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "AT", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=AT", + "buy": [ + { + "displayPriority": 3, + "logoPath": "/seGSXajazLMCKGB5hnRCidtjay1.jpg", + "id": 10, + "name": "Amazon Video" + }, + { + "displayPriority": 4, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 7, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 9, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + }, + { + "displayPriority": 12, + "logoPath": "/5vfrJQgNe9UnHVgVNAwZTy0Jo9o.jpg", + "id": 68, + "name": "Microsoft Store" + }, + { + "displayPriority": 14, + "logoPath": "/cBN4jd4wPq6on0kESiTlevqvlnL.jpg", + "id": 20, + "name": "maxdome Store" + }, + { + "displayPriority": 21, + "logoPath": "/6AKbY2ayaEuH4zKg2prqoVQ9iaY.jpg", + "id": 130, + "name": "Sky Store" + } + ], + "flatrate": [ + { + "displayPriority": 2, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "AU", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=AU", + "buy": [ + { + "displayPriority": 10, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 13, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 17, + "logoPath": "/5vfrJQgNe9UnHVgVNAwZTy0Jo9o.jpg", + "id": 68, + "name": "Microsoft Store" + }, + { + "displayPriority": 21, + "logoPath": "/pTnn5JwWr4p3pG8H6VrpiQo7Vs0.jpg", + "id": 192, + "name": "YouTube" + } + ], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "AZ", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=AZ", + "buy": [ + { + "displayPriority": 1, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + } + ], + "flatrate": [] + }, + { + "iso_3166_1": "BA", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=BA", + "buy": [ + { + "displayPriority": 8, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + } + ], + "flatrate": [ + { + "displayPriority": 7, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "BB", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=BB", + "buy": [], + "flatrate": [ + { + "displayPriority": 26, + "logoPath": "/fksCUZ9QDWZMUwL2LgMtLckROUN.jpg", + "id": 1899, + "name": "Max" + } + ] + }, + { + "iso_3166_1": "BE", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=BE", + "buy": [ + { + "displayPriority": 6, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 7, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 10, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + } + ], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "BG", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=BG", + "buy": [ + { + "displayPriority": 2, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + } + ], + "flatrate": [ + { + "displayPriority": 16, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "BO", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=BO", + "buy": [], + "flatrate": [ + { + "displayPriority": 2, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "BR", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=BR", + "buy": [], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "BS", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=BS", + "buy": [], + "flatrate": [ + { + "displayPriority": 27, + "logoPath": "/fksCUZ9QDWZMUwL2LgMtLckROUN.jpg", + "id": 1899, + "name": "Max" + } + ] + }, + { + "iso_3166_1": "BZ", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=BZ", + "buy": [], + "flatrate": [ + { + "displayPriority": 2, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "CA", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=CA", + "buy": [ + { + "displayPriority": 6, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 8, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 23, + "logoPath": "/5vfrJQgNe9UnHVgVNAwZTy0Jo9o.jpg", + "id": 68, + "name": "Microsoft Store" + }, + { + "displayPriority": 37, + "logoPath": "/pTnn5JwWr4p3pG8H6VrpiQo7Vs0.jpg", + "id": 192, + "name": "YouTube" + } + ], + "flatrate": [ + { + "displayPriority": 1, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + }, + { + "displayPriority": 4, + "logoPath": "/ewOptMVIYcOadMGGJz8DJueH2bH.jpg", + "id": 230, + "name": "Crave" + }, + { + "displayPriority": 5, + "logoPath": "/csPQMbeJWY7bjwWruZjtc27xf2l.jpg", + "id": 305, + "name": "Crave Starz" + } + ] + }, + { + "iso_3166_1": "CH", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=CH", + "buy": [ + { + "displayPriority": 3, + "logoPath": "/47klot430ytIqldQUUx2avN45Sr.jpg", + "id": 150, + "name": "blue TV" + }, + { + "displayPriority": 4, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 5, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 6, + "logoPath": "/5vfrJQgNe9UnHVgVNAwZTy0Jo9o.jpg", + "id": 68, + "name": "Microsoft Store" + }, + { + "displayPriority": 9, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + }, + { + "displayPriority": 16, + "logoPath": "/6AKbY2ayaEuH4zKg2prqoVQ9iaY.jpg", + "id": 130, + "name": "Sky Store" + } + ], + "flatrate": [ + { + "displayPriority": 2, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + }, + { + "displayPriority": 36, + "logoPath": "/l5SFTF3l9l1KSgZJM7foAppUenz.jpg", + "id": 622, + "name": "UPC TV" + } + ] + }, + { + "iso_3166_1": "CL", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=CL", + "buy": [ + { + "displayPriority": 4, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + } + ], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + }, + { + "displayPriority": 8, + "logoPath": "/tRNA2CRgA4XHvd7Mx9dH3sFtDVb.jpg", + "id": 339, + "name": "MovistarTV" + } + ] + }, + { + "iso_3166_1": "CO", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=CO", + "buy": [ + { + "displayPriority": 3, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + } + ], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + }, + { + "displayPriority": 8, + "logoPath": "/tRNA2CRgA4XHvd7Mx9dH3sFtDVb.jpg", + "id": 339, + "name": "MovistarTV" + } + ] + }, + { + "iso_3166_1": "CR", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=CR", + "buy": [], + "flatrate": [ + { + "displayPriority": 2, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "CV", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=CV", + "buy": [ + { + "displayPriority": 12, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + } + ], + "flatrate": [] + }, + { + "iso_3166_1": "CY", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=CY", + "buy": [ + { + "displayPriority": 0, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 1, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + } + ], + "flatrate": [] + }, + { + "iso_3166_1": "CZ", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=CZ", + "buy": [ + { + "displayPriority": 4, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 5, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 35, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + } + ], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "DE", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=DE", + "buy": [ + { + "displayPriority": 4, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 8, + "logoPath": "/seGSXajazLMCKGB5hnRCidtjay1.jpg", + "id": 10, + "name": "Amazon Video" + }, + { + "displayPriority": 11, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 14, + "logoPath": "/pTnn5JwWr4p3pG8H6VrpiQo7Vs0.jpg", + "id": 192, + "name": "YouTube" + }, + { + "displayPriority": 15, + "logoPath": "/6AKbY2ayaEuH4zKg2prqoVQ9iaY.jpg", + "id": 130, + "name": "Sky Store" + }, + { + "displayPriority": 16, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + }, + { + "displayPriority": 18, + "logoPath": "/cBN4jd4wPq6on0kESiTlevqvlnL.jpg", + "id": 20, + "name": "maxdome Store" + }, + { + "displayPriority": 26, + "logoPath": "/lrEigPPAhggq02q53uM8vdWAIUX.jpg", + "id": 178, + "name": "MagentaTV" + }, + { + "displayPriority": 32, + "logoPath": "/5vfrJQgNe9UnHVgVNAwZTy0Jo9o.jpg", + "id": 68, + "name": "Microsoft Store" + }, + { + "displayPriority": 148, + "logoPath": "/dKh2TJ9lTWV0UIcDQGMnMyQ8AIN.jpg", + "id": 1993, + "name": "Videoload" + } + ], + "flatrate": [ + { + "displayPriority": 2, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "DK", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=DK", + "buy": [ + { + "displayPriority": 4, + "logoPath": "/bnoTnLzz2MAhK3Yc6P9KXe5drIz.jpg", + "id": 76, + "name": "Viaplay" + }, + { + "displayPriority": 5, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 6, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 8, + "logoPath": "/5vfrJQgNe9UnHVgVNAwZTy0Jo9o.jpg", + "id": 68, + "name": "Microsoft Store" + }, + { + "displayPriority": 15, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + }, + { + "displayPriority": 17, + "logoPath": "/auWzottqpExOHUlJwDZ4DvdHGoL.jpg", + "id": 423, + "name": "Blockbuster" + }, + { + "displayPriority": 18, + "logoPath": "/sNmIwrniZZORvNJOmEuWGhwBJNf.jpg", + "id": 426, + "name": "SF Anytime" + } + ], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "DO", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=DO", + "buy": [], + "flatrate": [ + { + "displayPriority": 25, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "EC", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=EC", + "buy": [ + { + "displayPriority": 9, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + } + ], + "flatrate": [ + { + "displayPriority": 1, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "EE", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=EE", + "buy": [ + { + "displayPriority": 2, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 3, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + } + ], + "flatrate": [ + { + "displayPriority": 23, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "EG", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=EG", + "buy": [ + { + "displayPriority": 2, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 4, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + } + ], + "flatrate": [ + { + "displayPriority": 46, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "ES", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=ES", + "buy": [ + { + "displayPriority": 3, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 12, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + }, + { + "displayPriority": 14, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 17, + "logoPath": "/5vfrJQgNe9UnHVgVNAwZTy0Jo9o.jpg", + "id": 68, + "name": "Microsoft Store" + } + ], + "flatrate": [ + { + "displayPriority": 1, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "FI", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=FI", + "buy": [ + { + "displayPriority": 4, + "logoPath": "/bnoTnLzz2MAhK3Yc6P9KXe5drIz.jpg", + "id": 76, + "name": "Viaplay" + }, + { + "displayPriority": 8, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 9, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 10, + "logoPath": "/5vfrJQgNe9UnHVgVNAwZTy0Jo9o.jpg", + "id": 68, + "name": "Microsoft Store" + }, + { + "displayPriority": 14, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + }, + { + "displayPriority": 15, + "logoPath": "/auWzottqpExOHUlJwDZ4DvdHGoL.jpg", + "id": 423, + "name": "Blockbuster" + }, + { + "displayPriority": 16, + "logoPath": "/sNmIwrniZZORvNJOmEuWGhwBJNf.jpg", + "id": 426, + "name": "SF Anytime" + } + ], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "FR", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=FR", + "buy": [ + { + "displayPriority": 4, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 8, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 15, + "logoPath": "/5vfrJQgNe9UnHVgVNAwZTy0Jo9o.jpg", + "id": 68, + "name": "Microsoft Store" + }, + { + "displayPriority": 16, + "logoPath": "/pTnn5JwWr4p3pG8H6VrpiQo7Vs0.jpg", + "id": 192, + "name": "YouTube" + }, + { + "displayPriority": 21, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + }, + { + "displayPriority": 120, + "logoPath": "/cAUTwxqoI7zc9XtYr1SMtWZEsXg.jpg", + "id": 58, + "name": "Canal VOD" + } + ], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "GB", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=GB", + "buy": [ + { + "displayPriority": 4, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 14, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 16, + "logoPath": "/6AKbY2ayaEuH4zKg2prqoVQ9iaY.jpg", + "id": 130, + "name": "Sky Store" + }, + { + "displayPriority": 17, + "logoPath": "/5vfrJQgNe9UnHVgVNAwZTy0Jo9o.jpg", + "id": 68, + "name": "Microsoft Store" + }, + { + "displayPriority": 21, + "logoPath": "/pTnn5JwWr4p3pG8H6VrpiQo7Vs0.jpg", + "id": 192, + "name": "YouTube" + }, + { + "displayPriority": 27, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + } + ], + "flatrate": [ + { + "displayPriority": 1, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "GH", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=GH", + "buy": [ + { + "displayPriority": 15, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + } + ], + "flatrate": [] + }, + { + "iso_3166_1": "GR", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=GR", + "buy": [ + { + "displayPriority": 2, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 3, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + } + ], + "flatrate": [ + { + "displayPriority": 20, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "GT", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=GT", + "buy": [], + "flatrate": [ + { + "displayPriority": 1, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "HK", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=HK", + "buy": [], + "flatrate": [ + { + "displayPriority": 33, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "HN", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=HN", + "buy": [], + "flatrate": [ + { + "displayPriority": 2, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + }, + { + "displayPriority": 33, + "logoPath": "/fksCUZ9QDWZMUwL2LgMtLckROUN.jpg", + "id": 1899, + "name": "Max" + } + ] + }, + { + "iso_3166_1": "HR", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=HR", + "buy": [ + { + "displayPriority": 3, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + } + ], + "flatrate": [ + { + "displayPriority": 32, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "HU", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=HU", + "buy": [ + { + "displayPriority": 3, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 4, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 28, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + } + ], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "ID", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=ID", + "buy": [], + "flatrate": [ + { + "displayPriority": 2, + "logoPath": "/zdTSUEVZFXp3E0EkOMGN99QPVJp.jpg", + "id": 122, + "name": "Hotstar" + } + ] + }, + { + "iso_3166_1": "IE", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=IE", + "buy": [ + { + "displayPriority": 4, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 7, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 9, + "logoPath": "/6AKbY2ayaEuH4zKg2prqoVQ9iaY.jpg", + "id": 130, + "name": "Sky Store" + }, + { + "displayPriority": 12, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + }, + { + "displayPriority": 14, + "logoPath": "/5vfrJQgNe9UnHVgVNAwZTy0Jo9o.jpg", + "id": 68, + "name": "Microsoft Store" + } + ], + "flatrate": [ + { + "displayPriority": 2, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "IL", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=IL", + "buy": [ + { + "displayPriority": 25, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + } + ], + "flatrate": [] + }, + { + "iso_3166_1": "IN", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=IN", + "buy": [ + { + "displayPriority": 3, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 9, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 12, + "logoPath": "/pTnn5JwWr4p3pG8H6VrpiQo7Vs0.jpg", + "id": 192, + "name": "YouTube" + } + ], + "flatrate": [ + { + "displayPriority": 5, + "logoPath": "/zdTSUEVZFXp3E0EkOMGN99QPVJp.jpg", + "id": 122, + "name": "Hotstar" + } + ] + }, + { + "iso_3166_1": "IS", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=IS", + "buy": [ + { + "displayPriority": 4, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 26, + "logoPath": "/bnoTnLzz2MAhK3Yc6P9KXe5drIz.jpg", + "id": 76, + "name": "Viaplay" + } + ], + "flatrate": [ + { + "displayPriority": 1, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "IT", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=IT", + "buy": [ + { + "displayPriority": 3, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + }, + { + "displayPriority": 4, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 7, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 22, + "logoPath": "/5vfrJQgNe9UnHVgVNAwZTy0Jo9o.jpg", + "id": 68, + "name": "Microsoft Store" + } + ], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "JM", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=JM", + "buy": [], + "flatrate": [ + { + "displayPriority": 27, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "JP", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=JP", + "buy": [ + { + "displayPriority": 5, + "logoPath": "/seGSXajazLMCKGB5hnRCidtjay1.jpg", + "id": 10, + "name": "Amazon Video" + }, + { + "displayPriority": 6, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 8, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + } + ], + "flatrate": [ + { + "displayPriority": 2, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "KR", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=KR", + "buy": [ + { + "displayPriority": 3, + "logoPath": "/hPcjSaWfMwEqXaCMu7Fkb529Dkc.jpg", + "id": 356, + "name": "wavve" + }, + { + "displayPriority": 6, + "logoPath": "/crFbxg6jkiKc14gpIGMkre9Y3mu.jpg", + "id": 96, + "name": "Naver Store" + }, + { + "displayPriority": 7, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + } + ], + "flatrate": [ + { + "displayPriority": 2, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "LC", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=LC", + "buy": [], + "flatrate": [ + { + "displayPriority": 14, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "LI", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=LI", + "buy": [], + "flatrate": [ + { + "displayPriority": 26, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "LT", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=LT", + "buy": [ + { + "displayPriority": 2, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 3, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + } + ], + "flatrate": [ + { + "displayPriority": 24, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "LU", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=LU", + "buy": [ + { + "displayPriority": 0, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 1, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 2, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + } + ], + "flatrate": [ + { + "displayPriority": 3, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "LV", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=LV", + "buy": [ + { + "displayPriority": 2, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 3, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + } + ], + "flatrate": [ + { + "displayPriority": 23, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "ME", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=ME", + "buy": [], + "flatrate": [ + { + "displayPriority": 1, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "MK", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=MK", + "buy": [ + { + "displayPriority": 8, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + } + ], + "flatrate": [ + { + "displayPriority": 7, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "MT", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=MT", + "buy": [ + { + "displayPriority": 8, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + } + ], + "flatrate": [ + { + "displayPriority": 7, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "MU", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=MU", + "buy": [ + { + "displayPriority": 14, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + } + ], + "flatrate": [] + }, + { + "iso_3166_1": "MX", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=MX", + "buy": [], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "MY", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=MY", + "buy": [], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/zdTSUEVZFXp3E0EkOMGN99QPVJp.jpg", + "id": 122, + "name": "Hotstar" + } + ] + }, + { + "iso_3166_1": "MZ", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=MZ", + "buy": [ + { + "displayPriority": 15, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + } + ], + "flatrate": [] + }, + { + "iso_3166_1": "NI", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=NI", + "buy": [], + "flatrate": [ + { + "displayPriority": 2, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + }, + { + "displayPriority": 14, + "logoPath": "/fksCUZ9QDWZMUwL2LgMtLckROUN.jpg", + "id": 1899, + "name": "Max" + } + ] + }, + { + "iso_3166_1": "NL", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=NL", + "buy": [ + { + "displayPriority": 7, + "logoPath": "/lnOkJKyFl87mzqNebAbE8ADdHoB.jpg", + "id": 71, + "name": "Pathé Thuis" + }, + { + "displayPriority": 9, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 13, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 14, + "logoPath": "/5vfrJQgNe9UnHVgVNAwZTy0Jo9o.jpg", + "id": 68, + "name": "Microsoft Store" + }, + { + "displayPriority": 15, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + } + ], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + }, + { + "displayPriority": 33, + "logoPath": "/nzjqsR28PVVp5GJAlHtbBArNKqN.jpg", + "id": 563, + "name": "KPN" + } + ] + }, + { + "iso_3166_1": "NO", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=NO", + "buy": [ + { + "displayPriority": 4, + "logoPath": "/bnoTnLzz2MAhK3Yc6P9KXe5drIz.jpg", + "id": 76, + "name": "Viaplay" + }, + { + "displayPriority": 5, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 8, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 9, + "logoPath": "/5vfrJQgNe9UnHVgVNAwZTy0Jo9o.jpg", + "id": 68, + "name": "Microsoft Store" + }, + { + "displayPriority": 15, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + }, + { + "displayPriority": 18, + "logoPath": "/sNmIwrniZZORvNJOmEuWGhwBJNf.jpg", + "id": 426, + "name": "SF Anytime" + } + ], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "NZ", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=NZ", + "buy": [ + { + "displayPriority": 4, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 6, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 9, + "logoPath": "/5vfrJQgNe9UnHVgVNAwZTy0Jo9o.jpg", + "id": 68, + "name": "Microsoft Store" + } + ], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "PA", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=PA", + "buy": [], + "flatrate": [ + { + "displayPriority": 28, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "PE", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=PE", + "buy": [ + { + "displayPriority": 4, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + } + ], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "PH", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=PH", + "buy": [], + "flatrate": [ + { + "displayPriority": 31, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "PL", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=PL", + "buy": [ + { + "displayPriority": 2, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 4, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 8, + "logoPath": "/pTnn5JwWr4p3pG8H6VrpiQo7Vs0.jpg", + "id": 192, + "name": "YouTube" + }, + { + "displayPriority": 38, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + } + ], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "PT", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=PT", + "buy": [ + { + "displayPriority": 4, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 5, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 7, + "logoPath": "/pTnn5JwWr4p3pG8H6VrpiQo7Vs0.jpg", + "id": 192, + "name": "YouTube" + }, + { + "displayPriority": 8, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + } + ], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "PY", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=PY", + "buy": [], + "flatrate": [ + { + "displayPriority": 2, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "RO", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=RO", + "buy": [ + { + "displayPriority": 24, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + } + ], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "RS", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=RS", + "buy": [ + { + "displayPriority": 8, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + } + ], + "flatrate": [ + { + "displayPriority": 7, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + }, + { + "displayPriority": 31, + "logoPath": "/fksCUZ9QDWZMUwL2LgMtLckROUN.jpg", + "id": 1899, + "name": "Max" + } + ] + }, + { + "iso_3166_1": "RU", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=RU", + "buy": [ + { + "displayPriority": 22, + "logoPath": "/51wuCkUdkEQTUtB8TrtZzzxp3Tj.jpg", + "id": 117, + "name": "Kinopoisk" + } + ], + "flatrate": [ + { + "displayPriority": 22, + "logoPath": "/51wuCkUdkEQTUtB8TrtZzzxp3Tj.jpg", + "id": 117, + "name": "Kinopoisk" + } + ] + }, + { + "iso_3166_1": "SA", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=SA", + "buy": [ + { + "displayPriority": 3, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + } + ], + "flatrate": [] + }, + { + "iso_3166_1": "SE", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=SE", + "buy": [ + { + "displayPriority": 4, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 6, + "logoPath": "/bnoTnLzz2MAhK3Yc6P9KXe5drIz.jpg", + "id": 76, + "name": "Viaplay" + }, + { + "displayPriority": 8, + "logoPath": "/5vfrJQgNe9UnHVgVNAwZTy0Jo9o.jpg", + "id": 68, + "name": "Microsoft Store" + }, + { + "displayPriority": 9, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 16, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + }, + { + "displayPriority": 18, + "logoPath": "/sNmIwrniZZORvNJOmEuWGhwBJNf.jpg", + "id": 426, + "name": "SF Anytime" + } + ], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "SG", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=SG", + "buy": [], + "flatrate": [ + { + "displayPriority": 2, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "SI", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=SI", + "buy": [ + { + "displayPriority": 8, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + }, + { + "displayPriority": 26, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + } + ], + "flatrate": [ + { + "displayPriority": 7, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "SK", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=SK", + "buy": [ + { + "displayPriority": 4, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 6, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 40, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + } + ], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "SM", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=SM", + "buy": [], + "flatrate": [ + { + "displayPriority": 26, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "SV", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=SV", + "buy": [], + "flatrate": [ + { + "displayPriority": 28, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "TH", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=TH", + "buy": [], + "flatrate": [ + { + "displayPriority": 0, + "logoPath": "/zdTSUEVZFXp3E0EkOMGN99QPVJp.jpg", + "id": 122, + "name": "Hotstar" + } + ] + }, + { + "iso_3166_1": "TR", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=TR", + "buy": [ + { + "displayPriority": 5, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 6, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + } + ], + "flatrate": [ + { + "displayPriority": 9, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "TT", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=TT", + "buy": [], + "flatrate": [ + { + "displayPriority": 13, + "logoPath": "/fksCUZ9QDWZMUwL2LgMtLckROUN.jpg", + "id": 1899, + "name": "Max" + }, + { + "displayPriority": 15, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "TW", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=TW", + "buy": [], + "flatrate": [ + { + "displayPriority": 2, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "UA", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=UA", + "buy": [ + { + "displayPriority": 2, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 3, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 4, + "logoPath": "/bZvc9dXrXNly7cA0V4D9pR8yJwm.jpg", + "id": 35, + "name": "Rakuten TV" + } + ], + "flatrate": [] + }, + { + "iso_3166_1": "UG", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=UG", + "buy": [ + { + "displayPriority": 15, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + } + ], + "flatrate": [] + }, + { + "iso_3166_1": "US", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=US", + "buy": [ + { + "displayPriority": 4, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 16, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + }, + { + "displayPriority": 17, + "logoPath": "/pTnn5JwWr4p3pG8H6VrpiQo7Vs0.jpg", + "id": 192, + "name": "YouTube" + }, + { + "displayPriority": 40, + "logoPath": "/i6lRmkKmJ23oOZ6IyjnOYLKxA9J.jpg", + "id": 7, + "name": "Fandango At Home" + }, + { + "displayPriority": 49, + "logoPath": "/5vfrJQgNe9UnHVgVNAwZTy0Jo9o.jpg", + "id": 68, + "name": "Microsoft Store" + }, + { + "displayPriority": 115, + "logoPath": "/1g3ULbVMEW8OVKOJymLvfboCrMv.jpg", + "id": 352, + "name": "AMC on Demand" + } + ], + "flatrate": [ + { + "displayPriority": 6, + "logoPath": "/bxBlRPEPpMVDc4jMhSrTf2339DW.jpg", + "id": 15, + "name": "Hulu" + }, + { + "displayPriority": 9, + "logoPath": "/fksCUZ9QDWZMUwL2LgMtLckROUN.jpg", + "id": 1899, + "name": "Max" + } + ] + }, + { + "iso_3166_1": "UY", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=UY", + "buy": [], + "flatrate": [ + { + "displayPriority": 28, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "VE", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=VE", + "buy": [ + { + "displayPriority": 3, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + } + ], + "flatrate": [ + { + "displayPriority": 1, + "logoPath": "/97yvRBw1GzX7fXprcF80er19ot.jpg", + "id": 337, + "name": "Disney Plus" + } + ] + }, + { + "iso_3166_1": "ZA", + "link": "https://www.themoviedb.org/movie/679-aliens/watch?locale=ZA", + "buy": [ + { + "displayPriority": 2, + "logoPath": "/9ghgSC0MA082EL6HLCW3GalykFD.jpg", + "id": 2, + "name": "Apple TV" + }, + { + "displayPriority": 3, + "logoPath": "/8z7rC8uIDaTM91X0ZfkRf04ydj2.jpg", + "id": 3, + "name": "Google Play Movies" + } + ], + "flatrate": [] + } + ], + "keywords": [ + { + "id": 803, + "name": "android" + }, + { + "id": 1826, + "name": "space marine" + }, + { + "id": 1603, + "name": "extraterrestrial technology" + }, + { + "id": 2345, + "name": "spaceman" + }, + { + "id": 3801, + "name": "space travel" + }, + { + "id": 3591, + "name": "settler" + }, + { + "id": 4090, + "name": "colony" + }, + { + "id": 3385, + "name": "cryogenics" + }, + { + "id": 3387, + "name": "vacuum" + }, + { + "id": 3388, + "name": "space colony" + }, + { + "id": 3389, + "name": "warrior woman" + }, + { + "id": 9951, + "name": "alien" + }, + { + "id": 9882, + "name": "space" + }, + { + "id": 11322, + "name": "female protagonist" + }, + { + "id": 13031, + "name": "creature" + }, + { + "id": 17937, + "name": "desolate" + }, + { + "id": 160137, + "name": "female hero" + }, + { + "id": 226177, + "name": "xenomorph" + }, + { + "id": 301079, + "name": "desolate planet" + }, + { + "id": 318171, + "name": "strong female protagonist" + }, + { + "id": 325812, + "name": "exhilarated" + } + ] +} \ No newline at end of file diff --git a/src/lib/rules/__tests__/rules.ts b/src/lib/rules/__tests__/rules.ts new file mode 100644 index 0000000..2f500c1 --- /dev/null +++ b/src/lib/rules/__tests__/rules.ts @@ -0,0 +1,329 @@ +import {MovieDetails} from "@core/api/overseerr/interfaces"; +import {Rule} from "@core/lib/rules"; +import {PredicateFactory} from "@core/lib/rules/factory"; +import {FalsePredicate, TruePredicate, VoteCountPredicate} from "@core/lib/rules/predicate"; +import {GenresPredicate} from "@core/lib/rules/predicate/genres"; +import {CastPredicate} from "@core/lib/rules/predicate/cast"; +import {CrewPredicate} from "@core/lib/rules/predicate/crew"; +import {KeywordPredicate} from "@core/lib/rules/predicate/keywords"; +import {AndPredicate} from "@core/lib/rules/predicate/and"; +import {OrPredicate} from "@core/lib/rules/predicate/or"; + +const movie = require('./movie.json') as MovieDetails; + +PredicateFactory.init() + +describe('truePredicate', () => { + const rule = new Rule( + 'simple', + [new TruePredicate()], + 'accept') + + it('should match', async () => { + const response = rule.matches(movie); + expect(response).toEqual(true) + }) +}) + +describe('falsePredicate', () => { + const rule = new Rule( + 'simple', + [new FalsePredicate()], + 'accept') + + it('should not match', async () => { + const response = rule.matches(movie); + expect(response).toEqual(false) + }) +}) + +describe('defaultPredicate', () => { + it('should match when all children match', async () => { + const rule = new Rule( + 'simple', + [new TruePredicate(), new TruePredicate()], + 'accept') + + const response = rule.matches(movie); + expect(response).toEqual(true) + }) + + it('should not match when any child does not', async () => { + const rule = new Rule( + 'simple', + [new TruePredicate(), new FalsePredicate()], + 'accept') + const response = rule.matches(movie); + expect(response).toEqual(false) + }) +}) + +describe('andPredicate', () => { + it('should match when all children match', async () => { + const rule = new Rule( + 'simple', + [new AndPredicate([ + new TruePredicate(), + new TruePredicate() + ])], + 'accept') + + const response = rule.matches(movie); + expect(response).toEqual(true) + }) + + it('should not match when any child does not', async () => { + const rule = new Rule( + 'simple', + [new AndPredicate([ + new TruePredicate(), + new FalsePredicate()] + )], + 'accept') + const response = rule.matches(movie); + expect(response).toEqual(false) + }) +}) + +describe('orPredicate', () => { + it('should match when all children match', async () => { + const rule = new Rule( + 'simple', + [new OrPredicate([ + new TruePredicate(), + new TruePredicate() + ])], + 'accept') + + const response = rule.matches(movie); + expect(response).toEqual(true) + }) + + it('should match when at least one child matches', async () => { + const rule = new Rule( + 'simple', + [new OrPredicate([ + new TruePredicate(), + new FalsePredicate()] + )], + 'accept') + const response = rule.matches(movie); + expect(response).toEqual(true) + }) + + it('should not match when no child matches', async () => { + const rule = new Rule( + 'simple', + [new OrPredicate([ + new FalsePredicate(), + new FalsePredicate()] + )], + 'accept') + const response = rule.matches(movie); + expect(response).toEqual(false) + }) + + it('should not match when vote count below 8554 or above 8556', async () => { + { + const rule = new Rule( + 'simple', + [new VoteCountPredicate({threshold: 8554, operator: 'lt'})], + 'accept') + const response = rule.matches(movie); + expect(response).toEqual(false) + } + { + const rule = new Rule( + 'simple', + [new VoteCountPredicate({threshold: 8556, operator: 'gt'})], + 'accept') + const response = rule.matches(movie); + expect(response).toEqual(false) + } + }) + + it('should match when vote count above 8555', async () => { + { + const rule = new Rule( + 'simple', + [new VoteCountPredicate({threshold: 8554, operator: 'gt'})], + 'accept') + const response = rule.matches(movie); + expect(response).toEqual(true) + } + { + const rule = new Rule( + 'simple', + [new VoteCountPredicate({threshold: 8556, operator: 'lt'})], + 'accept') + const response = rule.matches(movie); + expect(response).toEqual(true) + } + }) + + it('should match genders', async () => { + { + const rule = new Rule( + 'simple', + [new GenresPredicate({terms: ['science-fiction']})], + 'accept') + const response = rule.matches(movie); + expect(response).toEqual(true) + } + { + const rule = new Rule( + 'simple', + [new GenresPredicate({terms: ['comédie']})], + 'accept') + const response = rule.matches(movie); + expect(response).toEqual(false) + } + }) +}) + +describe('castPredicate', () => { + it('should match', async () => { + const rule = new Rule( + 'simple', + [new CastPredicate({terms: ['Sigourney Weaver']})], + 'accept') + + const response = rule.matches(movie); + expect(response).toEqual(true) + }) + + it('should match despite different case', async () => { + const rule = new Rule( + 'simple', + [new CastPredicate({terms: ['SiGoUrnEy weAVEr']})], + 'accept') + + const response = rule.matches(movie); + expect(response).toEqual(true) + }) + + it('should match if at least one cast match', async () => { + const rule = new Rule( + 'simple', + [new CastPredicate({terms: ['Sigourney Weaver', 'Jessica Alba']})], + 'accept') + + const response = rule.matches(movie); + expect(response).toEqual(true) + }) + + it('should not match', async () => { + const rule = new Rule( + 'simple', + [new CastPredicate({terms: ['Jessica Alba']})], + 'accept') + + const response = rule.matches(movie); + expect(response).toEqual(false) + }) +}) + + +describe('crewPredicate', () => { + it('should match', async () => { + const rule = new Rule( + 'simple', + [new CrewPredicate({terms: ['James Cameron']})], + 'accept') + + const response = rule.matches(movie); + expect(response).toEqual(true) + }) + + it('should match with a job', async () => { + const rule = new Rule( + 'simple', + [new CrewPredicate({job: 'director', terms: ['James Cameron']})], + 'accept') + + const response = rule.matches(movie); + expect(response).toEqual(true) + }) + + it('should match despite different case', async () => { + const rule = new Rule( + 'simple', + [new CrewPredicate({terms: ['JaMEs CamerOn']})], + 'accept') + + const response = rule.matches(movie); + expect(response).toEqual(true) + }) + + it('should match if at least one cast match', async () => { + const rule = new Rule( + 'simple', + [new CrewPredicate({job: 'Art Direction', terms: ['Bert Davey', 'Ken Court']})], + 'accept') + + const response = rule.matches(movie); + expect(response).toEqual(true) + }) + + it('should not match', async () => { + const rule = new Rule( + 'simple', + [new CrewPredicate({terms: ['Jessica Alba']})], + 'accept') + + const response = rule.matches(movie); + expect(response).toEqual(false) + }) + + it('should not match when person found in another job', async () => { + const rule = new Rule( + 'simple', + [new CrewPredicate({job: 'director', terms: ['Peter Lamont']})], + 'accept') + + const response = rule.matches(movie); + expect(response).toEqual(false) + }) +}) + +describe('keywordPredicate', () => { + it('should match', async () => { + const rule = new Rule( + 'simple', + [new KeywordPredicate({terms: ['space travel']})], + 'accept') + + const response = rule.matches(movie); + expect(response).toEqual(true) + }) + + it('should match despite different case', async () => { + const rule = new Rule( + 'simple', + [new KeywordPredicate({terms: ['SpAcE TraVel']})], + 'accept') + + const response = rule.matches(movie); + expect(response).toEqual(true) + }) + + it('should match if at least one cast match', async () => { + const rule = new Rule( + 'simple', + [new KeywordPredicate({terms: ['space travel', 'unknown keyword']})], + 'accept') + + const response = rule.matches(movie); + expect(response).toEqual(true) + }) + + it('should not match', async () => { + const rule = new Rule( + 'simple', + [new KeywordPredicate({terms: ['unknown keyword']})], + 'accept') + + const response = rule.matches(movie); + expect(response).toEqual(false) + }) +}) \ No newline at end of file diff --git a/src/lib/rules/factory.ts b/src/lib/rules/factory.ts new file mode 100644 index 0000000..f63ec22 --- /dev/null +++ b/src/lib/rules/factory.ts @@ -0,0 +1,66 @@ +import {Predicate, PredicateBuilder, PredicateKey} from "@core/lib/rules/index"; +import {PredicateOption} from "@core/lib/rules/interfaces"; +import {AgePredicateBuilder, ScorePredicateBuilder, VoteCountPredicateBuilder} from "@core/lib/rules/predicate"; +import {WatchProvidersPredicateBuilder} from "@core/lib/rules/predicate/watchproviders"; +import {GenresPredicateBuilder} from "@core/lib/rules/predicate/genres"; +import {CastPredicateBuilder} from "@core/lib/rules/predicate/cast"; +import {CrewPredicateBuilder} from "@core/lib/rules/predicate/crew"; +import {ProductionCompanyPredicateBuilder} from "@core/lib/rules/predicate/productionCompany"; +import {ReleasedPredicateBuilder} from "@core/lib/rules/predicate/released"; +import {KeywordPredicateBuilder} from "@core/lib/rules/predicate/keywords"; +import {OrPredicateBuilder} from "@core/lib/rules/predicate/or"; +import {AndPredicateBuilder} from "@core/lib/rules/predicate/and"; +import {NotPredicateBuilder} from "@core/lib/rules/predicate/not"; +import logger from "@core/log"; + +class PredicateFactoryClass { + private builders: Map; + + constructor() { + this.builders = new Map(); + } + + public buildPredicates(predicates: PredicateOption[]): Predicate[] { + return predicates.map(p => { + const key = Object.keys(p)[0]; + const data = (p as any)[key]; + return this.getBuilder(key).build(data); + }); + } + + public init() { + logger.debug('Initialized PredicateFactory') + } + + private getBuilder(key: string): PredicateBuilder { + if (!this.builders.has(key)) { + throw new Error(`No predicate builder found for key '${key}'`) + } + return this.builders.get(key)!; + } + + public registerBuilder(builder: PredicateBuilder) { + if (this.builders.has(builder.key)) { + throw new Error(`Duplicate predicate builder for key '${builder.key}'`) + } + this.builders.set(builder.key, builder); + } +} + +export const PredicateFactory = new PredicateFactoryClass(); +const builders = [ + AndPredicateBuilder, + OrPredicateBuilder, + NotPredicateBuilder, + AgePredicateBuilder, + ScorePredicateBuilder, + VoteCountPredicateBuilder, + WatchProvidersPredicateBuilder, + GenresPredicateBuilder, + CastPredicateBuilder, + CrewPredicateBuilder, + ProductionCompanyPredicateBuilder, + ReleasedPredicateBuilder, + KeywordPredicateBuilder +]; +builders.forEach(b => PredicateFactory.registerBuilder(b)) \ No newline at end of file diff --git a/src/lib/rules/index.ts b/src/lib/rules/index.ts new file mode 100644 index 0000000..6568e21 --- /dev/null +++ b/src/lib/rules/index.ts @@ -0,0 +1,48 @@ +import {MovieDetails} from "@core/api/overseerr/interfaces"; +import {RuleAction} from "@core/lib/rules/interfaces"; + +export abstract class Predicate { + abstract matches(movie: MovieDetails): boolean; +} +export type PredicateKey = string; + +export interface PredicateBuilder { + key: PredicateKey; + build(data: any): Predicate; +} + +export type RuleResult = + | 'accept' + | 'reject' + | 'skip' + +export type RuleEvaluation = { + rule?: Rule; + movie: MovieDetails, + result: RuleResult +} + +export class Rule { + public name: string; + public filter: Predicate[]; + public action: RuleAction; + + constructor(name: string, filter: Predicate[], action: RuleAction) { + this.name = name; + this.filter = filter; + this.action = action; + } + + public matches(movie: MovieDetails): boolean { + return this.filter.every(p => p.matches(movie)); + } + + public apply(movie: MovieDetails): RuleEvaluation { + const match = this.matches(movie); + return { + rule: this, + movie: movie, + result: match ? this.action : 'skip' + } + } +} diff --git a/src/lib/rules/interfaces.ts b/src/lib/rules/interfaces.ts new file mode 100644 index 0000000..3040da4 --- /dev/null +++ b/src/lib/rules/interfaces.ts @@ -0,0 +1,35 @@ +export type RuleAction = 'accept' | 'reject' + +export interface RulesetOptions { + name: string; + extends: string; + rules: RuleOptions[]; +} + +export interface RuleOptions { + name: string; + whenMatch: PredicateOption[]; + action: RuleAction; +} + +export type PredicateOption = + | AndFilterOptions + | OrFilterOptions + | NotFilterOptions + | FilterOptions + +export interface FilterOptions { + [key: string]: string|string[]; +} + +export interface AndFilterOptions { + and: FilterOptions[] +} + +export interface OrFilterOptions { + or: FilterOptions[] +} + +export interface NotFilterOptions { + not: FilterOptions[] +} diff --git a/src/lib/rules/predicate/and.ts b/src/lib/rules/predicate/and.ts new file mode 100644 index 0000000..62e071d --- /dev/null +++ b/src/lib/rules/predicate/and.ts @@ -0,0 +1,21 @@ +import {Predicate, PredicateBuilder} from "@core/lib/rules"; +import {MovieDetails} from "@core/api/overseerr/interfaces"; +import {PredicateOption} from "@core/lib/rules/interfaces"; +import {GroupPredicate} from "@core/lib/rules/predicate/group"; +import {PredicateFactory} from "@core/lib/rules/factory"; + +export class AndPredicate extends GroupPredicate { + + constructor(children: Predicate[]) { + super(children); + } + + matches(movie: MovieDetails): boolean { + return this.children.every(p => p.matches(movie)); + } +} + +export const AndPredicateBuilder: PredicateBuilder = { + key: 'and', + build: (data: any) => new AndPredicate(PredicateFactory.buildPredicates(data as PredicateOption[])), +}; \ No newline at end of file diff --git a/src/lib/rules/predicate/boolean.ts b/src/lib/rules/predicate/boolean.ts new file mode 100644 index 0000000..29d76dc --- /dev/null +++ b/src/lib/rules/predicate/boolean.ts @@ -0,0 +1,23 @@ +import {Predicate} from "@core/lib/rules"; + +export interface BooleanPredicateOptions { + value: string; +} + +export abstract class BooleanPredicate extends Predicate { + protected targetValue: boolean; + + protected constructor(options: BooleanPredicateOptions) { + super(); + this.targetValue = fromHumanReadableBoolean(options.value); + } +} + +const fromHumanReadableBoolean = (str: string): boolean => { + const acceptableTrueBoolean = [ + 'yes', + 'true', + '1' + ] + return acceptableTrueBoolean.includes(str); +} diff --git a/src/lib/rules/predicate/cast.ts b/src/lib/rules/predicate/cast.ts new file mode 100644 index 0000000..0e842c1 --- /dev/null +++ b/src/lib/rules/predicate/cast.ts @@ -0,0 +1,36 @@ +import {MovieDetails} from "@core/api/overseerr/interfaces"; +import TagsPredicate, {TagsPredicateOptions} from "@core/lib/rules/predicate/tag"; +import {PredicateBuilder} from "@core/lib/rules"; + +export type CastOptions = { + +} & TagsPredicateOptions; + +export class CastPredicate extends TagsPredicate { + + constructor(options: CastOptions) { + super(options); + } + + getTags(movie: MovieDetails): string[] { + if (!movie.credits || !movie.credits.cast) { + return [] + } + const tags: string[] = []; + for (const cast of movie.credits.cast) { + tags.push(cast.name); + } + return tags; + } + +} + +export const CastPredicateBuilder:PredicateBuilder = { + key: 'cast', + build: (data:string[]) => { + if (!Array.isArray(data)) { + throw new Error('Error while building Cast filter. Expecting a list of name...') + } + return new CastPredicate({ terms: data }) + } +} \ No newline at end of file diff --git a/src/lib/rules/predicate/crew.ts b/src/lib/rules/predicate/crew.ts new file mode 100644 index 0000000..c0131ab --- /dev/null +++ b/src/lib/rules/predicate/crew.ts @@ -0,0 +1,45 @@ +import {MovieDetails} from "@core/api/overseerr/interfaces"; +import {PredicateBuilder} from "@core/lib/rules"; +import TagsPredicate, {TagsPredicateOptions} from "@core/lib/rules/predicate/tag"; + +export type CrewOptions = { + job?: string; +} & TagsPredicateOptions; + +export class CrewPredicate extends TagsPredicate { + private job?: string; + + constructor(options: CrewOptions) { + super(options); + this.job = options.job?.toLowerCase() ?? undefined; + } + + getTags(movie: MovieDetails): string[] { + if (!movie.credits || !movie.credits.crew) { + return [] + } + const tags: string[] = []; + for (const crew of movie.credits.crew) { + if (!this.job || this.job === crew.job.toLowerCase()) { + tags.push(crew.name); + } + } + return tags; + } + +} + +export type CrewPredicateBuilderData = { + job?: string; + names: string[]; +}; + +export const CrewPredicateBuilder:PredicateBuilder = { + key: 'crew', + build: (data: string[] | CrewPredicateBuilderData) => { + if (Array.isArray(data)) { + return new CrewPredicate({ terms: data }) + } + return new CrewPredicate({ job: data.job, terms: data.names }) + } +} \ No newline at end of file diff --git a/src/lib/rules/predicate/genres.ts b/src/lib/rules/predicate/genres.ts new file mode 100644 index 0000000..94230b4 --- /dev/null +++ b/src/lib/rules/predicate/genres.ts @@ -0,0 +1,24 @@ +import {MovieDetails} from "@core/api/overseerr/interfaces"; +import {PredicateBuilder} from "@core/lib/rules"; +import TagsPredicate, {TagsPredicateOptions} from "@core/lib/rules/predicate/tag"; + +export class GenresPredicate extends TagsPredicate { + + constructor(options: TagsPredicateOptions) { + super(options); + } + + getTags(movie: MovieDetails): string[] { + if (!movie.genres) { + return [] + } + + return movie.genres.map(g => g.name.toLowerCase()); + } + +} + +export const GenresPredicateBuilder:PredicateBuilder = { + key: 'genres', + build: (genres: string[]) => new GenresPredicate({terms: genres}) +} \ No newline at end of file diff --git a/src/lib/rules/predicate/group.ts b/src/lib/rules/predicate/group.ts new file mode 100644 index 0000000..c05a76b --- /dev/null +++ b/src/lib/rules/predicate/group.ts @@ -0,0 +1,10 @@ +import {Predicate} from "@core/lib/rules"; + +export abstract class GroupPredicate extends Predicate { + protected children: Predicate[]; + + protected constructor(children: Predicate[]) { + super(); + this.children = children; + } +} \ No newline at end of file diff --git a/src/lib/rules/predicate/index.ts b/src/lib/rules/predicate/index.ts new file mode 100644 index 0000000..0579d12 --- /dev/null +++ b/src/lib/rules/predicate/index.ts @@ -0,0 +1,62 @@ +import {fromHumanReadableDuration} from "@core/lib/rules/predicate/time"; +import {Predicate, PredicateBuilder} from "@core/lib/rules"; +import {MovieDetails} from "@core/api/overseerr/interfaces"; +import {fromHumanReadableNumber, NumberPredicate, NumberPredicateOptions} from "@core/lib/rules/predicate/number"; + +export class TruePredicate extends Predicate { + matches(movie: MovieDetails): boolean { + return true; + } +} + +export class FalsePredicate extends Predicate { + matches(movie: MovieDetails): boolean { + return false; + } +} + +export class AgePredicate extends NumberPredicate { + constructor(options: NumberPredicateOptions) { + super(options); + } + + getMeasurement(movie: MovieDetails): number { + return (Date.now() - Date.parse(movie.releaseDate)) / 1000; + } +} + +export const AgePredicateBuilder:PredicateBuilder = { + key: 'age', + build: (data: string) => new AgePredicate(fromHumanReadableDuration(data)) +} + + +export class ScorePredicate extends NumberPredicate { + constructor(options: NumberPredicateOptions) { + super(options); + } + + getMeasurement(movie: MovieDetails): number { + return movie.voteAverage; + } +} + +export const ScorePredicateBuilder:PredicateBuilder = { + key: 'score', + build: (data: string) => new ScorePredicate(fromHumanReadableNumber(data)) +} + +export class VoteCountPredicate extends NumberPredicate { + constructor(options: NumberPredicateOptions) { + super(options); + } + + getMeasurement(movie: MovieDetails): number { + return movie.voteCount; + } +} + +export const VoteCountPredicateBuilder:PredicateBuilder = { + key: 'voteCount', + build: (data: string) => new VoteCountPredicate(fromHumanReadableNumber(data)) +} \ No newline at end of file diff --git a/src/lib/rules/predicate/keywords.ts b/src/lib/rules/predicate/keywords.ts new file mode 100644 index 0000000..db9adde --- /dev/null +++ b/src/lib/rules/predicate/keywords.ts @@ -0,0 +1,36 @@ +import {MovieDetails} from "@core/api/overseerr/interfaces"; +import {PredicateBuilder} from "@core/lib/rules"; +import TagsPredicate, {TagsPredicateOptions} from "@core/lib/rules/predicate/tag"; + +export type KeywordOptions = { + +} & TagsPredicateOptions; + +export class KeywordPredicate extends TagsPredicate { + + constructor(options: KeywordOptions) { + super(options); + } + + getTags(movie: MovieDetails): string[] { + if (!movie.keywords) { + return [] + } + const tags: string[] = []; + for (const keywords of movie.keywords) { + tags.push(keywords.name); + } + return tags; + } + +} + +export const KeywordPredicateBuilder:PredicateBuilder = { + key: 'keyword', + build: (data:string[]) => { + if (!Array.isArray(data)) { + throw new Error('Error while building Keyword filter. Expecting a list of keyword...') + } + return new KeywordPredicate({ terms: data }) + } +} \ No newline at end of file diff --git a/src/lib/rules/predicate/not.ts b/src/lib/rules/predicate/not.ts new file mode 100644 index 0000000..29523bd --- /dev/null +++ b/src/lib/rules/predicate/not.ts @@ -0,0 +1,20 @@ +import {Predicate, PredicateBuilder} from "@core/lib/rules"; +import {PredicateFactory} from "@core/lib/rules/factory"; +import {MovieDetails} from "@core/api/overseerr/interfaces"; +import {PredicateOption} from "@core/lib/rules/interfaces"; +import {GroupPredicate} from "@core/lib/rules/predicate/group"; + +export class NotPredicate extends GroupPredicate { + constructor(children: Predicate[]) { + super(children); + } + + matches(movie: MovieDetails): boolean { + return !this.children.every(p => p.matches(movie)); + } +} + +export const NotPredicateBuilder: PredicateBuilder = { + key: 'not', + build: (data: any) => new NotPredicate(PredicateFactory.buildPredicates(data as PredicateOption[])) +}; \ No newline at end of file diff --git a/src/lib/rules/predicate/number.ts b/src/lib/rules/predicate/number.ts new file mode 100644 index 0000000..540f452 --- /dev/null +++ b/src/lib/rules/predicate/number.ts @@ -0,0 +1,42 @@ +import {Predicate} from "@core/lib/rules"; +import {MovieDetails} from "@core/api/overseerr/interfaces"; + +export interface NumberPredicateOptions { + threshold: number; + operator: 'lt' | 'gt'; +} + +export abstract class NumberPredicate extends Predicate { + private threshold: number; + private operator: 'lt' | 'gt'; + + constructor(options: NumberPredicateOptions) { + super(); + this.threshold = options.threshold; + this.operator = options.operator; + } + + abstract getMeasurement(movie: MovieDetails): number; + + matches(movie: MovieDetails): boolean { + const measuredSeconds = this.getMeasurement(movie); + if (this.operator === 'lt') { + return measuredSeconds < this.threshold; + } + return measuredSeconds > this.threshold; + } +} + +export const fromHumanReadableNumber = (str: string): {threshold: number, operator: 'lt' | 'gt' } => { + const regex = /(below|under|less than|above|greater than) (\d+(\.\d+)?)/gm; + const groups = [...str.matchAll(regex)][0] + if (!groups) { + throw new Error(`Unparsable number string '${str}' !`); + } + const operator = groups[1]; + const threshold = Number(groups[2]); + return { + operator: ['below', 'under', 'less than'].includes(operator) ? 'lt' : 'gt', + threshold: threshold + } +} \ No newline at end of file diff --git a/src/lib/rules/predicate/or.ts b/src/lib/rules/predicate/or.ts new file mode 100644 index 0000000..158c289 --- /dev/null +++ b/src/lib/rules/predicate/or.ts @@ -0,0 +1,20 @@ +import {Predicate, PredicateBuilder} from "@core/lib/rules"; +import {PredicateFactory} from "@core/lib/rules/factory"; +import {MovieDetails} from "@core/api/overseerr/interfaces"; +import {PredicateOption} from "@core/lib/rules/interfaces"; +import {GroupPredicate} from "@core/lib/rules/predicate/group"; + +export class OrPredicate extends GroupPredicate { + constructor(children: Predicate[]) { + super(children); + } + + matches(movie: MovieDetails): boolean { + return this.children.some(p => p.matches(movie)); + } +} + +export const OrPredicateBuilder: PredicateBuilder = { + key: 'or', + build: (data: any) => new OrPredicate(PredicateFactory.buildPredicates(data as PredicateOption[])), +}; \ No newline at end of file diff --git a/src/lib/rules/predicate/productionCompany.ts b/src/lib/rules/predicate/productionCompany.ts new file mode 100644 index 0000000..bd76d2d --- /dev/null +++ b/src/lib/rules/predicate/productionCompany.ts @@ -0,0 +1,36 @@ +import {MovieDetails} from "@core/api/overseerr/interfaces"; +import {PredicateBuilder} from "@core/lib/rules"; +import TagsPredicate, {TagsPredicateOptions} from "@core/lib/rules/predicate/tag"; + +export type ProductionCompanyOptions = { + +} & TagsPredicateOptions; + +export class ProductionCompanyPredicate extends TagsPredicate { + + constructor(options: ProductionCompanyOptions) { + super(options); + } + + getTags(movie: MovieDetails): string[] { + if (!movie.productionCompanies) { + return [] + } + const tags: string[] = []; + for (const company of movie.productionCompanies) { + tags.push(company.name); + } + return tags; + } + +} + +export const ProductionCompanyPredicateBuilder:PredicateBuilder = { + key: 'productionCompany', + build: (data: string[]) => { + if (!Array.isArray(data)) { + throw new Error('Error while building ProductionCompany filter. Expecting a list of company names...') + } + return new ProductionCompanyPredicate({terms: data}) + } +} \ No newline at end of file diff --git a/src/lib/rules/predicate/released.ts b/src/lib/rules/predicate/released.ts new file mode 100644 index 0000000..3bed128 --- /dev/null +++ b/src/lib/rules/predicate/released.ts @@ -0,0 +1,32 @@ +import {PredicateBuilder} from "@core/lib/rules"; +import {MovieDetails} from "@core/api/overseerr/interfaces"; +import {BooleanPredicate, BooleanPredicateOptions} from "@core/lib/rules/predicate/boolean"; +import {TagsPredicateOptions} from "@core/lib/rules/predicate/tag"; + +export type ReleasedOptions = { + +} & BooleanPredicateOptions; + +export class ReleasedPredicate extends BooleanPredicate { + + constructor(options: ReleasedOptions) { + super(options); + } + + matches(movie: MovieDetails): boolean { + if (movie.status) { + const released = movie.status.toLowerCase() === 'released'; + return released == this.targetValue; + } else if (movie.releaseDate) { + const movieReleaseDate = new Date(movie.releaseDate); + const released = movieReleaseDate.getTime() < Date.now(); + return released == this.targetValue; + } + return false; + } +} + +export const ReleasedPredicateBuilder:PredicateBuilder = { + key: 'released', + build: (data: string) => new ReleasedPredicate({ value: data }) +} diff --git a/src/lib/rules/predicate/tag.ts b/src/lib/rules/predicate/tag.ts new file mode 100644 index 0000000..344b64e --- /dev/null +++ b/src/lib/rules/predicate/tag.ts @@ -0,0 +1,24 @@ +import {Predicate} from "@core/lib/rules"; +import {MovieDetails} from "@core/api/overseerr/interfaces"; + +export type TagsPredicateOptions = { + terms: string[] +}; + +export abstract class TagsPredicate extends Predicate { + private terms: string[]; + + protected constructor(options: TagsPredicateOptions) { + super(); + this.terms = options.terms; + } + + abstract getTags(movie: MovieDetails): string[]; + + matches(movie: MovieDetails): boolean { + const tags = this.getTags(movie).map(e => e.toLowerCase()); + return this.terms.some(t => tags.includes(t.toLowerCase())); + } +} + +export default TagsPredicate; diff --git a/src/lib/rules/predicate/time.ts b/src/lib/rules/predicate/time.ts new file mode 100644 index 0000000..b7b8d24 --- /dev/null +++ b/src/lib/rules/predicate/time.ts @@ -0,0 +1,29 @@ +import {NumberPredicate, NumberPredicateOptions} from "@core/lib/rules/predicate/number"; + +export type TimePredicateOptions = NumberPredicateOptions; + +export abstract class TimePredicate extends NumberPredicate { + + protected constructor(options: TimePredicateOptions) { + super(options); + } +} + +export const fromHumanReadableDuration = (str: string): {threshold: number, operator: 'lt' | 'gt' } => { + const regex = /(less than|more than) (\d+(\.\d+)?) (years|year|days|day|month|months|weeks|week)/gm; + const groups = [...str.matchAll(regex)][0] + const operator = groups[1]; + const threshold: number = Number(groups[2]); + const units = groups[4].endsWith('s') ? groups[4] : `${groups[4]}s`; + const scale: Record = { + hours: 60 * 60, + days: 24 * 60 * 60, + weeks: 7 * 24 * 60 * 60, + months: 30 * 24 * 60 * 60, + years: 365 * 24 * 60 * 60 + } + return { + operator: operator === 'less than' ? 'lt' : 'gt', + threshold: threshold * scale[units] + } +} \ No newline at end of file diff --git a/src/lib/rules/predicate/watchproviders.ts b/src/lib/rules/predicate/watchproviders.ts new file mode 100644 index 0000000..3ab00bb --- /dev/null +++ b/src/lib/rules/predicate/watchproviders.ts @@ -0,0 +1,37 @@ +import {MovieDetails} from "@core/api/overseerr/interfaces"; +import {PredicateBuilder} from "@core/lib/rules"; +import TagsPredicate, {TagsPredicateOptions} from "@core/lib/rules/predicate/tag"; + +export type WatchprovidersOptions = { + region: string; +} & TagsPredicateOptions; + +export class WatchprovidersPredicate extends TagsPredicate { + private region: string; + + constructor(options: WatchprovidersOptions) { + super(options); + this.region = options.region.toLowerCase(); + } + + getTags(movie: MovieDetails): string[] { + if (!movie.watchProviders) { + return [] + } + const tags: string[] = []; + for (const region of movie.watchProviders) { + if (region.iso_3166_1.toLowerCase() === this.region) { + if (region.flatrate) { + tags.push(...region.flatrate.map(r => r.name)); + } + } + } + return tags; + } + +} + +export const WatchProvidersPredicateBuilder:PredicateBuilder = { + key: 'watchProviders', + build: (data: {region: string, names: string[]}) => new WatchprovidersPredicate({ region: data.region, terms: data.names}) +} \ No newline at end of file diff --git a/src/lib/ruleset.ts b/src/lib/ruleset.ts new file mode 100644 index 0000000..f0b3e68 --- /dev/null +++ b/src/lib/ruleset.ts @@ -0,0 +1,98 @@ +import logger from "../log"; +import {Rule, RuleEvaluation} from "@core/lib/rules"; +import {RuleAction, RulesetOptions} from "@core/lib/rules/interfaces"; +import {MovieDetails} from "@core/api/overseerr/interfaces"; +import Settings from "@core/lib/settings"; +import {PredicateFactory} from "@core/lib/rules/factory"; + +class Ruleset { + private _rulesetOptions: RulesetOptions; + private _rules: Rule[]; + private initialized: boolean; + + constructor(options: RulesetOptions) { + this.initialized = false; + this._rulesetOptions = options; + this._rules = []; + } + + public load(): Ruleset { + if (this.initialized) { + return this; + } + try { + if (process.env.NODE_ENV !== 'production') { + console.log(this._rulesetOptions) + } + this.buildRules(); + this.initialized = true; + logger.info(`Successfully loaded ruleset '${this.name}' with ${this._rules.length} rules`) + } catch (e) { + logger.error(e); + } + return this; + } + + get name() { + return this._rulesetOptions.name; + } + + get extendedRulesetName(): string | undefined { + return this._rulesetOptions.extends; + } + + private buildRules(): void { + this._rules = this._rulesetOptions.rules.map(o => new Rule(o.name, PredicateFactory.buildPredicates(o.whenMatch), o.action)) + } + + public evaluateRules(movie: MovieDetails): RuleEvaluation { + if (this.extendedRulesetName) { + const extendedRuleSet = getRuleset(this.extendedRulesetName); + const extendedEvaluation = extendedRuleSet.evaluateRules(movie); + if (extendedEvaluation.result !== 'skip') { + return extendedEvaluation; + } + } + + for (const rule of this._rules) { + const evaluation = rule.apply(movie); + if (evaluation.result !== 'skip') { + return evaluation; + } + } + return { movie, result: 'skip'}; + } + + get rules(): Rule[] { + return this._rules; + } +} + +let rulesets: Map; + +export const loadRulesets = (settings: Settings): void => { + try { + rulesets = new Map(); + const configuration = settings.load().rulesets; + for (let options of configuration) { + if (rulesets.has(options.name)) { + throw new Error(`Ruleset '${options.name}' already defined`) + } + + const ruleset = new Ruleset(options); + ruleset.load() + rulesets.set(ruleset.name, ruleset); + } + } catch(e: any) { + console.log(e); + } +} + +export const getRuleset = (name: string): Ruleset => { + if (!rulesets.has(name)) { + throw new Error(`Ruleset '${name}' does not exist`) + } + return rulesets.get(name); +} + +export default Ruleset; diff --git a/src/lib/settings.ts b/src/lib/settings.ts new file mode 100644 index 0000000..2b00487 --- /dev/null +++ b/src/lib/settings.ts @@ -0,0 +1,152 @@ +import fs from 'fs'; +import path from 'path'; +import yaml from 'js-yaml'; +import logger from "@core/log"; +import {RulesetOptions} from "@core/lib/rules/interfaces"; +import Ajv from "ajv"; + +const SCHEMA_PATH = './schema/schema.json'; + +const SETTINGS_PATH = process.env.CONFIG_DIRECTORY + ? `${process.env.CONFIG_DIRECTORY}/settings.yaml` + : path.join(__dirname, '../../config/settings.yaml'); + +export interface YamlSettings { + config: CrawlrrSettings +} + +export interface CrawlrrSettings { + overseerr: OverseerSettings; + plex?: PlexSettings; + discovery?: DiscoverySettings; + smartRecommendations?: SmartRecommendationsSettings; + rulesets: RulesetOptions[] +} + +export interface RulesetConsumer { + ruleset?: string; +} + +export interface OverseerSettings { + apiUrl: string; + user?: string; + password?: string; + dryRun?: boolean; +} + +export interface PlexSettings { + apiUrl: string; + plexToken: string; +} + +export interface DiscoverySettings extends RulesetConsumer{ + cron?: string; + streams?: string[]; +} + +export interface SmartRecommendationsSettings extends RulesetConsumer{ + cron: string; + plexLibrary: string; + minimumRating: number; +} + +export interface OnSettingLoadedCallback { + (settings: Settings): Promise; +} + +class Settings { + private _data: CrawlrrSettings; + private initialized: boolean; + private callback: OnSettingLoadedCallback | undefined; + + constructor() { + this.initialized = false; + this._data = { + overseerr: { + apiUrl: '' + }, + rulesets: [] + }; + } + + public load(): Settings { + if (this.initialized) { + return this; + } + logger.info('Loading settings...'); + + if (!fs.existsSync(SETTINGS_PATH)) { + throw new Error(`Could not find configuration file '${SETTINGS_PATH}' !`); + } + + try { + const yamlSettings = yaml.load(fs.readFileSync(SETTINGS_PATH, 'utf8')) as YamlSettings; + const schema = JSON.parse(fs.readFileSync(SCHEMA_PATH, 'utf8')); + const validator = new Ajv(); + const isValid = validator.validate(schema, yamlSettings); + + if (!isValid) { + throw new Error(`Invalid configuration file. ${JSON.stringify(validator.errors)}`); + } + + this._data = yamlSettings.config as CrawlrrSettings; + if (process.env.NODE_ENV !== 'production') { + console.log(this._data) + } + this.initialized = true; + logger.info(`Settings loaded successfully from ${SETTINGS_PATH}`); + if (this.callback) { + this.callback(this); + } + } catch (e) { + logger.error(e); + } + return this; + } + + private reload = () => { + logger.info(`Configuration file changed. Reloading...`); + this.initialized = false; + this.load(); + } + + public watch(callback: OnSettingLoadedCallback) { + fs.watchFile(SETTINGS_PATH, this.reload); + this.callback = callback; + return this; + } + + get overseerr(): OverseerSettings { + return this._data.overseerr; + } + + get plex(): PlexSettings | null { + return this._data.plex ?? null; + } + + get rulesets(): RulesetOptions[] { + return this._data.rulesets; + } + + get discovery(): DiscoverySettings { + return this._data.discovery ?? { + cron: '? ? ? * * *', + streams: ['upcoming'] + }; + } + + get smartRecommendations(): SmartRecommendationsSettings | null { + return this._data.smartRecommendations ?? null; + } +} + +let settings: Settings | undefined; + +export const getSettings = (): Settings => { + if (!settings) { + settings = new Settings(); + } + return settings; +} + +export default Settings; diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000..7456e9d --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,71 @@ +import {MovieResult} from "@core/api/overseerr/interfaces"; + +export const isFulfilled = (v: PromiseSettledResult): v is PromiseFulfilledResult => v.status === 'fulfilled'; + +export const isMovie = (m: MovieResult) => m.mediaType === 'movie'; + +export const distinctMovies = (acc: MovieResult[], movie: MovieResult) => { + if (!acc.find((m: MovieResult) => m.id === movie.id)) { + acc.push(movie); + } + return acc; +} + +enum EscapeCode { + FG_BLACK = '\x1b[30m', + FG_RED = '\x1b[31m', + FG_GREEN = '\x1b[32m', + FG_YELLOW = '\x1b[33m', + FG_BLUE = '\x1b[34m', + FG_MAGENTA = '\x1b[35m', + FG_CYAN = '\x1b[36m', + FG_WHITE = '\x1b[37m', + RESET = '\x1b[0m' +} + +export enum Icon { + CHECK = '\u2714' +} + +const colorize = (color: EscapeCode, str: string): string => color + str + EscapeCode.RESET; + +export const color = { + black: (str: string): string => colorize(EscapeCode.FG_BLACK, str), + red: (str: string): string => colorize(EscapeCode.FG_RED, str), + green: (str: string): string => colorize(EscapeCode.FG_GREEN, str), + yellow: (str: string): string => colorize(EscapeCode.FG_YELLOW, str), + blue: (str: string): string => colorize(EscapeCode.FG_BLUE, str), + magenta: (str: string): string => colorize(EscapeCode.FG_MAGENTA, str), + cyan: (str: string): string => colorize(EscapeCode.FG_CYAN, str), + white: (str: string): string => colorize(EscapeCode.FG_WHITE, str), +} + +type ErrorWithMessage = { + message: string +} + +function isErrorWithMessage(error: unknown): error is ErrorWithMessage { + return ( + typeof error === 'object' && + error !== null && + 'message' in error && + typeof (error as Record).message === 'string' + ) +} + + +function toErrorWithMessage(maybeError: unknown): ErrorWithMessage { + if (isErrorWithMessage(maybeError)) return maybeError + + try { + return new Error(JSON.stringify(maybeError)) + } catch { + // fallback in case there's an error stringifying the maybeError + // like with circular references for example. + return new Error(String(maybeError)) + } +} + +export function getErrorMessage(error: unknown) { + return toErrorWithMessage(error).message +} \ No newline at end of file diff --git a/src/log.ts b/src/log.ts new file mode 100644 index 0000000..8622192 --- /dev/null +++ b/src/log.ts @@ -0,0 +1,21 @@ +import {createLogger, format, transports} from 'winston'; + +const logFormat = format.printf(({ timestamp, level, message }) => { + return `[ ${timestamp.replace(/[TZ]/gm, ' ')}] ${level}: ${message}`; +}) + +const logger = createLogger({ + level: process.env.NODE_ENV === 'production' ? 'info' : 'debug', + format: format.combine( + format.errors({ stack: true }), + format.timestamp(), + format.colorize(), + format.prettyPrint(), + format.simple(), + logFormat + ), + transports: [new transports.Console()], + exceptionHandlers: [new transports.Console()], +}); + +export default logger \ No newline at end of file diff --git a/src/routes/index.ts b/src/routes/index.ts new file mode 100644 index 0000000..2fcde69 --- /dev/null +++ b/src/routes/index.ts @@ -0,0 +1,94 @@ +import express from 'express'; +import logger from "@core/log"; +import OverseerrApi from "@core/api/overseerr"; +import ruleset, {getRuleset} from "@core/lib/ruleset"; +import {discover, smartRecommendations} from "@core/service"; +import {getErrorMessage} from "@core/lib/utils"; + +export const apiRouter = express.Router(); + +apiRouter.use('/healthcheck', async (req, res, next) => { + res.status(200).json({ + status: 'ok' + }) +}); + +apiRouter.use('/movies/upcoming', async (req, res, next) => { + const overseerr = new OverseerrApi(); + await overseerr.auth(); + const data = await overseerr.getUpcoming(); + res.status(200).json(data) +}); + +apiRouter.use('/movies/popular', async (req, res, next) => { + const overseerr = new OverseerrApi(); + await overseerr.auth(); + const data = await overseerr.getPopular(); + res.status(200).json(data) +}); + +apiRouter.use('/search', async (req, res, next) => { + const overseerr = new OverseerrApi(); + await overseerr.auth(); + const data = await overseerr.getUpcoming(); + res.status(200).json(data) +}); + +apiRouter.use('/movies/:id(\\d+)/evaluate/:ruleset', async (req, res, next) => { + try { + const overseerr = new OverseerrApi(); + await overseerr.auth(); + const movie = await overseerr.getMovie(Number(req.params.id)); + const result = getRuleset(req.params.ruleset).evaluateRules(movie); + res.status(200).json(result) + } catch (e: any) { + throw new Error(e); + } +}); + +apiRouter.use('/movies/:id(\\d+)/request', async (req, res, next) => { + const overseerr = new OverseerrApi(); + await overseerr.auth(); + const result = overseerr.requestMovie(Number(req.params.id)) + res.status(200).json(result) +}); + +apiRouter.use('/movies/:id(\\d+)', async (req, res, next) => { + const overseerr = new OverseerrApi(); + await overseerr.auth(); + const data = await overseerr.getMovie(Number(req.params.id)); + res.status(200).json(data) +}); + +apiRouter.use('/discover', async (req, res, next) => { + try { + await discover() + res.status(200).json({ + success: true, + message: 'Discovery complete !' + }) + } catch (e: unknown) { + res.status(500).json({ + success: false, + message: getErrorMessage(e) + }) + } +}); + +apiRouter.use('/smartRecommendations', async (req, res, next) => { + try { + await smartRecommendations(); + res.status(200).json({ + success: true, + message: 'Smart recommendations complete !' + }) + } catch (e: unknown) { + console.log(e) + res.status(500).json({ + success: false, + message: getErrorMessage(e) + }) + } +}); + +export default apiRouter; diff --git a/src/service/index.ts b/src/service/index.ts new file mode 100644 index 0000000..e3896ff --- /dev/null +++ b/src/service/index.ts @@ -0,0 +1,123 @@ +import logger from "@core/log"; +import {getSettings} from "@core/lib/settings"; +import OverseerrApi from "@core/api/overseerr"; +import {MediaStatus, MovieResult} from "@core/api/overseerr/interfaces"; +import Ruleset, {getRuleset} from "@core/lib/ruleset"; +import {color, distinctMovies, Icon, isFulfilled, isMovie} from "@core/lib/utils"; +import PlexApi from "@core/api/plex"; + +const processMovieResult = async (movies: MovieResult[], overseerr: OverseerrApi, ruleset: Ruleset, dryRun: boolean) => { + for (const movie of movies) { + if (movie.mediaInfo && movie.mediaInfo.status !== MediaStatus.UNKNOWN) { + logger.info(` Skipping - "${movie.title}" because it has already been processed`) + continue; + } + try { + const movieDetails = await overseerr.getMovie(movie.id); + if (movieDetails.mediaInfo?.requests) { + logger.info(` Skipping - "${movie.title}" because it already has requests`) + continue; + } + const ruleResult = ruleset.evaluateRules(movieDetails); + + if (ruleResult.result === 'accept') { + logger.info(color.green(Icon.CHECK + ' Adding') + ` - "${movieDetails.title}" because it matches rule "${color.green(ruleResult.rule?.name ?? '')}" `) + if (dryRun) { + logger.info(`Requesting movie ${movie.title} - Dry run`) + } else { + await overseerr.requestMovie(movie.id); + } + } else if (ruleResult.result === 'reject') { + logger.info(color.red(' Rejecting') + ` - "${movieDetails.title}" because it of rule "${color.blue(ruleResult.rule?.name ?? '')}" `) + } else { + logger.info(` Skipping - "${movieDetails.title}" doesn't match any rule`) + } + } catch (e) { + logger.error(`Error while analyzing movie ${movie.id} - ${movie.title}`, e) + } + } +} + +export const discover = async () => { + const overseerr = new OverseerrApi(); + await overseerr.auth(); + + const settings = getSettings().load().discovery; + const streams = settings.streams ?? ['upcoming', 'popular']; + if (!settings.ruleset) { + throw new Error(`No ruleset defined in Discovery section`) + } + const ruleset = getRuleset(settings.ruleset); + logger.info(`Using ruleset: ${ruleset.name}`); + + logger.info(` Searching into following streams: ${streams.join(', ')}`); + const results: Promise[] = streams.map(s => { + if (s.toLowerCase() == 'upcoming') { + return overseerr.getUpcoming(); + + } else if (s.toLowerCase() == 'trending') { + return overseerr.getTrending(); + + } else { + return overseerr.getPopular(); + } + }) ; + + const promiseResults = await Promise.allSettled(results); + const movies = promiseResults.filter(isFulfilled) + .flatMap((p: PromiseFulfilledResult) => p.value) + .filter(isMovie) + .reduce(distinctMovies, []) + + logger.info(`Found ${movies.length} to analyze...`) + const dryRun = getSettings().overseerr.dryRun ?? false; + + await processMovieResult(movies, overseerr, ruleset, dryRun); + + logger.info(`Discovery complete...`) +} + +export const smartRecommendations = async () => { + const overseerr = new OverseerrApi(); + await overseerr.auth(); + const plex = new PlexApi(); + + const settings = getSettings().load().smartRecommendations; + if (!settings) { + logger.info(`Missing configuration. Stopping...`) + return; + } + if (!settings.ruleset) { + logger.info(`No ruleset defined in Favorites Recommendations section`) + return; + } + const ruleset = getRuleset(settings.ruleset); + logger.info(`Using ruleset: ${ruleset.name}`); + const library = await plex.getLibrary(settings.plexLibrary); + if (!library) { + logger.info(`Cannot find Plex library '${settings.plexLibrary}'. Stopping...`) + return; + } + const movies = await plex.getFavoritesRecommendationsByLibrary(library.key, settings.minimumRating) + const moviesGuids = new Set(); + movies.map(v => plex.getGuid('tmdb', v)) + .forEach(g => { + if (g) { + moviesGuids.add(g); + } + }); + const dryRun = getSettings().overseerr.dryRun ?? false; + + const recommendationsResults = await Promise.allSettled( + Array.from(moviesGuids).map((guid: string) => overseerr.getRecommendationsByMovie(guid)) + ) + const recommendations = recommendationsResults.filter(isFulfilled) + .flatMap(r => r.value); + + // Remove duplicates + const recommendationsMap = new Map(recommendations.map((movie) => [movie.id, movie])); + const recommendedMovies = Array.from(recommendationsMap.values()); + + await processMovieResult(recommendedMovies, overseerr, ruleset, dryRun); + +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..5b39939 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,111 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "lib": ["es2020"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */ + "rootDir": "./src", /* Specify the root folder within your source files. */ + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + "baseUrl": "./src", /* Specify the base directory to resolve non-relative module names. */ + "paths": { + "@core/*": ["*"] + }, + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./build", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +}