From 3ee6545ac71e26c080a0bf2d3fb44dfa31b75be8 Mon Sep 17 00:00:00 2001 From: Vint Sanghyeok Lee Date: Sun, 5 Nov 2023 20:31:32 +0900 Subject: [PATCH] v2023 deployment: A new chapter begins. --- .devcontainer/Dockerfile | 4 + .devcontainer/devcontainer.json | 17 + .devcontainer/docker-compose.yml | 8 + .eslintrc.js | 16 + .github/workflows/cla.yaml | 29 + .gitignore | 4 + .vscode/launch.json | 18 + .vscode/tasks.json | 10 + Dockerfile | 30 + LICENSE | 99 + README.md | 36 + package.json | 19 + packages/api/codegen.ts | 18 + packages/api/package.json | 23 + packages/api/src/AppContext.ts | 7 + .../api/src/cursor/ArrayCursorConnection.ts | 174 + packages/api/src/index.ts | 6 + packages/api/src/logger.ts | 8 + packages/api/src/resolvers/carrier.ts | 60 + packages/api/src/resolvers/index.ts | 49 + packages/api/src/resolvers/track.ts | 413 + packages/api/src/schema/schema.graphql | 349 + packages/api/src/typeDefs.ts | 9 + packages/api/tsconfig.json | 11 + packages/cli/package.json | 25 + packages/cli/src/index.ts | 146 + packages/cli/src/logger.ts | 36 + packages/cli/tsconfig.json | 7 + packages/core/jest.config.js | 5 + packages/core/package.json | 35 + packages/core/src/CarrierAlias.ts | 27 + .../src/carrier-registry/CarrierRegistry.ts | 8 + .../DefaultCarrierRegistry.ts | 159 + packages/core/src/carrier-registry/index.ts | 2 + .../CarrierUpstreamFetcher.ts | 22 + .../src/carrier-upstream-fetcher/index.ts | 4 + .../core/src/carriers/de.dhl/DHLAPISchemas.ts | 243 + packages/core/src/carriers/de.dhl/index.ts | 397 + packages/core/src/carriers/jp.sagawa/index.ts | 181 + .../core/src/carriers/kr.chunilps/index.ts | 295 + .../kr.cjlogistics/CJLogisticsAPISchemas.ts | 90 + .../core/src/carriers/kr.cjlogistics/index.ts | 279 + .../carriers/kr.cvsnet/CVSnetAPISchemas.ts | 63 + packages/core/src/carriers/kr.cvsnet/index.ts | 236 + .../src/carriers/kr.cway/CwayAPISchemas.ts | 30 + packages/core/src/carriers/kr.cway/index.ts | 186 + packages/core/src/carriers/kr.daesin/index.ts | 279 + packages/core/src/carriers/kr.epost/index.ts | 229 + packages/core/src/carriers/kr.hanjin/index.ts | 345 + .../kr.honamlogis/HonamLogisAPISchemas.ts | 34 + .../core/src/carriers/kr.honamlogis/index.ts | 184 + .../kr.ilyanglogis/IlyangLogisAPISchemas.ts | 48 + .../core/src/carriers/kr.ilyanglogis/index.ts | 189 + .../kr.kdexp/KyungdongExpressAPISchemas.ts | 28 + packages/core/src/carriers/kr.kdexp/index.ts | 169 + .../core/src/carriers/kr.kunyoung/index.ts | 187 + packages/core/src/carriers/kr.logen/index.ts | 200 + packages/core/src/carriers/kr.lotte/index.ts | 200 + packages/core/src/carriers/kr.slx/index.ts | 175 + .../core/src/carriers/kr.todaypickup/index.ts | 212 + .../src/carriers/nl.tnt/TNTAPIAPISchemas.ts | 58 + packages/core/src/carriers/nl.tnt/index.ts | 170 + .../core/src/carriers/un.upu.ems/index.ts | 190 + packages/core/src/carriers/us.fedex/index.ts | 385 + packages/core/src/carriers/us.ups/index.ts | 192 + .../src/carriers/us.usps/USPSAPISchemas.ts | 200 + packages/core/src/carriers/us.usps/index.ts | 378 + packages/core/src/core/errors.ts | 22 + packages/core/src/core/index.ts | 1 + packages/core/src/core/interfaces.ts | 118 + packages/core/src/index.ts | 9 + packages/core/src/logger.ts | 5 + packages/core/tsconfig.json | 10 + packages/server/package.json | 25 + packages/server/src/index.ts | 93 + packages/server/src/logger.ts | 36 + packages/server/tsconfig.json | 7 + pnpm-lock.yaml | 7254 +++++++++++++++++ pnpm-workspace.yaml | 2 + tsconfig.base.json | 12 + tsconfig.json | 3 + 81 files changed, 15542 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/docker-compose.yml create mode 100644 .eslintrc.js create mode 100644 .github/workflows/cla.yaml create mode 100644 .gitignore create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 package.json create mode 100644 packages/api/codegen.ts create mode 100644 packages/api/package.json create mode 100644 packages/api/src/AppContext.ts create mode 100644 packages/api/src/cursor/ArrayCursorConnection.ts create mode 100644 packages/api/src/index.ts create mode 100644 packages/api/src/logger.ts create mode 100644 packages/api/src/resolvers/carrier.ts create mode 100644 packages/api/src/resolvers/index.ts create mode 100644 packages/api/src/resolvers/track.ts create mode 100644 packages/api/src/schema/schema.graphql create mode 100644 packages/api/src/typeDefs.ts create mode 100644 packages/api/tsconfig.json create mode 100644 packages/cli/package.json create mode 100644 packages/cli/src/index.ts create mode 100644 packages/cli/src/logger.ts create mode 100644 packages/cli/tsconfig.json create mode 100644 packages/core/jest.config.js create mode 100644 packages/core/package.json create mode 100644 packages/core/src/CarrierAlias.ts create mode 100644 packages/core/src/carrier-registry/CarrierRegistry.ts create mode 100644 packages/core/src/carrier-registry/DefaultCarrierRegistry.ts create mode 100644 packages/core/src/carrier-registry/index.ts create mode 100644 packages/core/src/carrier-upstream-fetcher/CarrierUpstreamFetcher.ts create mode 100644 packages/core/src/carrier-upstream-fetcher/index.ts create mode 100644 packages/core/src/carriers/de.dhl/DHLAPISchemas.ts create mode 100644 packages/core/src/carriers/de.dhl/index.ts create mode 100644 packages/core/src/carriers/jp.sagawa/index.ts create mode 100644 packages/core/src/carriers/kr.chunilps/index.ts create mode 100644 packages/core/src/carriers/kr.cjlogistics/CJLogisticsAPISchemas.ts create mode 100644 packages/core/src/carriers/kr.cjlogistics/index.ts create mode 100644 packages/core/src/carriers/kr.cvsnet/CVSnetAPISchemas.ts create mode 100644 packages/core/src/carriers/kr.cvsnet/index.ts create mode 100644 packages/core/src/carriers/kr.cway/CwayAPISchemas.ts create mode 100644 packages/core/src/carriers/kr.cway/index.ts create mode 100644 packages/core/src/carriers/kr.daesin/index.ts create mode 100644 packages/core/src/carriers/kr.epost/index.ts create mode 100644 packages/core/src/carriers/kr.hanjin/index.ts create mode 100644 packages/core/src/carriers/kr.honamlogis/HonamLogisAPISchemas.ts create mode 100644 packages/core/src/carriers/kr.honamlogis/index.ts create mode 100644 packages/core/src/carriers/kr.ilyanglogis/IlyangLogisAPISchemas.ts create mode 100644 packages/core/src/carriers/kr.ilyanglogis/index.ts create mode 100644 packages/core/src/carriers/kr.kdexp/KyungdongExpressAPISchemas.ts create mode 100644 packages/core/src/carriers/kr.kdexp/index.ts create mode 100644 packages/core/src/carriers/kr.kunyoung/index.ts create mode 100644 packages/core/src/carriers/kr.logen/index.ts create mode 100644 packages/core/src/carriers/kr.lotte/index.ts create mode 100644 packages/core/src/carriers/kr.slx/index.ts create mode 100644 packages/core/src/carriers/kr.todaypickup/index.ts create mode 100644 packages/core/src/carriers/nl.tnt/TNTAPIAPISchemas.ts create mode 100644 packages/core/src/carriers/nl.tnt/index.ts create mode 100644 packages/core/src/carriers/un.upu.ems/index.ts create mode 100644 packages/core/src/carriers/us.fedex/index.ts create mode 100644 packages/core/src/carriers/us.ups/index.ts create mode 100644 packages/core/src/carriers/us.usps/USPSAPISchemas.ts create mode 100644 packages/core/src/carriers/us.usps/index.ts create mode 100644 packages/core/src/core/errors.ts create mode 100644 packages/core/src/core/index.ts create mode 100644 packages/core/src/core/interfaces.ts create mode 100644 packages/core/src/index.ts create mode 100644 packages/core/src/logger.ts create mode 100644 packages/core/tsconfig.json create mode 100644 packages/server/package.json create mode 100644 packages/server/src/index.ts create mode 100644 packages/server/src/logger.ts create mode 100644 packages/server/tsconfig.json create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml create mode 100644 tsconfig.base.json create mode 100644 tsconfig.json diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..a45263e --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,4 @@ +FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:0-20 + +RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install 20" +RUN su node -c "npm install -g pnpm@8" diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..a30154c --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,17 @@ +{ + "dockerComposeFile": "docker-compose.yml", + "service": "devcontainer", + "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", + "forwardPorts": [4000], + "customizations": { + "vscode": { + "extensions": [ + ] + } + }, + "portsAttributes": { + "4000": { + "label": "api-server" + } + } +} \ No newline at end of file diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 0000000..38d103c --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,8 @@ +version: '3.8' +services: + devcontainer: + build: + context: . + volumes: + - ../..:/workspaces:cached + command: sleep infinity diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..443a271 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,16 @@ +module.exports = { + root: true, + env: { + es2021: true, + node: true, + }, + plugins: ["prettier"], + extends: ["standard-with-typescript", "plugin:prettier/recommended"], + overrides: [], + parserOptions: { + ecmaVersion: "latest", + project: ["tsconfig.json"], + sourceType: "module", + }, + rules: {}, +}; diff --git a/.github/workflows/cla.yaml b/.github/workflows/cla.yaml new file mode 100644 index 0000000..dce8628 --- /dev/null +++ b/.github/workflows/cla.yaml @@ -0,0 +1,29 @@ +name: "CLA Assistant" +on: + issue_comment: + types: [created] + pull_request_target: + types: [opened,closed,synchronize] + +permissions: + actions: write + contents: write + pull-requests: write + statuses: write + +jobs: + CLAAssistant: + runs-on: ubuntu-latest + steps: + - name: "CLA Assistant" + if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' + uses: contributor-assistant/github-action@v2.3.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PERSONAL_ACCESS_TOKEN: ${{ secrets.CLA_PERSONAL_ACCESS_TOKEN }} + with: + path-to-signatures: 'signatures/version1/cla.json' + path-to-document: 'https://tracker.delivery/policies/cla' + branch: 'main' + remote-organization-name: shlee322 + remote-repository-name: delivery-tracker-cla-signatures diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..17f7717 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +generated +.idea diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..2f2347f --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,18 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "@delivery-tracker/server", + "runtimeExecutable": "pnpm", + "cwd": "${workspaceFolder}/packages/server", + "runtimeArgs": ["run", "dev"], + "outputCapture": "std", + "env": { + "LISTEN_PORT": "4000" + }, + "preLaunchTask": "DeliveryTrackerServerPreLaunchTask" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..8175f99 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,10 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "DeliveryTrackerServerPreLaunchTask", + "type": "shell", + "command": "pnpm --filter '@delivery-tracker/server' build-with-deps" + } + ] + } \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..0fd719c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,30 @@ +FROM node:20-alpine AS base +RUN npm install -g pnpm@8 + +ENV PNPM_HOME="/pnpm" +ENV PATH="$PNPM_HOME:$PATH" + +FROM base AS prod-deps +WORKDIR /app +COPY . /app +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile + +FROM base AS build +RUN apk update && apk --no-cache add \ + python3 \ + make \ + g++ +WORKDIR /app +COPY . /app +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile +RUN pnpm --filter @delivery-tracker/server build-with-deps + +FROM prod-deps +COPY --from=build /app/packages/api/dist /app/packages/api/dist +COPY --from=build /app/packages/core/dist /app/packages/core/dist +COPY --from=build /app/packages/server/dist /app/packages/server/dist + +WORKDIR /app/packages/server + +ENV NODE_ENV=production +CMD ["pnpm", "start"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..082681e --- /dev/null +++ b/LICENSE @@ -0,0 +1,99 @@ +Copyright 2023 Vint Sanghyeok Lee + +Source code in this repository is covered by the Elastic License 2.0. +The default throughout the repository is a license under the Elastic License 2.0, +unless a file header or a license file in a subdirectory specifies another license. + +------ + +Elastic License 2.0 + +## Acceptance + +By using the software, you agree to all of the terms and conditions below. + +## Copyright License + +The licensor grants you a non-exclusive, royalty-free, worldwide, +non-sublicensable, non-transferable license to use, copy, distribute, make +available, and prepare derivative works of the software, in each case subject to +the limitations and conditions below. + +## Limitations + +You may not provide the software to third parties as a hosted or managed +service, where the service provides users with access to any substantial set of +the features or functionality of the software. + +You may not move, change, disable, or circumvent the license key functionality +in the software, and you may not remove or obscure any functionality in the +software that is protected by the license key. + +You may not alter, remove, or obscure any licensing, copyright, or other notices +of the licensor in the software. Any use of the licensor’s trademarks is subject +to applicable law. + +## Patents + +The licensor grants you a license, under any patent claims the licensor can +license, or becomes able to license, to make, have made, use, sell, offer for +sale, import and have imported the software, in each case subject to the +limitations and conditions in this license. This license does not cover any +patent claims that you cause to be infringed by modifications or additions to +the software. If you or your company make any written claim that the software +infringes or contributes to infringement of any patent, your patent license for +the software granted under these terms ends immediately. If your company makes +such a claim, your patent license ends immediately for work on behalf of your +company. + +## Notices + +You must ensure that anyone who gets a copy of any part of the software from you +also gets a copy of these terms. + +If you modify the software, you must include in any modified copies of the +software prominent notices stating that you have modified the software. + +## No Other Rights + +These terms do not imply any licenses other than those expressly granted in +these terms. + +## Termination + +If you use the software in violation of these terms, such use is not licensed, +and your licenses will automatically terminate. If the licensor provides you +with a notice of your violation, and you cease all violation of this license no +later than 30 days after you receive that notice, your licenses will be +reinstated retroactively. However, if you violate these terms after such +reinstatement, any additional violation of these terms will cause your licenses +to terminate automatically and permanently. + +## No Liability + +*As far as the law allows, the software comes as is, without any warranty or +condition, and the licensor will not be liable to you for any damages arising +out of these terms or the use or nature of the software, under any kind of +legal claim.* + +## Definitions + +The **licensor** is the entity offering these terms, and the **software** is the +software the licensor makes available under these terms, including any portion +of it. + +**you** refers to the individual or entity agreeing to these terms. + +**your company** is any legal entity, sole proprietorship, or other kind of +organization that you work for, plus all organizations that have control over, +are under the control of, or are under common control with that +organization. **control** means ownership of substantially all the assets of an +entity, or the power to direct its management and policies by vote, contract, or +otherwise. Control can be direct or indirect. + +**your licenses** are all the licenses granted to you for the software under +these terms. + +**use** means anything you do with the software requiring one of your licenses. + +**trademark** means trademarks, service marks, and similar rights. diff --git a/README.md b/README.md new file mode 100644 index 0000000..a70e1e2 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# Delivery Tracker + +Delivery and Shipping Tracking Service + +## Usage +### Cloud (Managed Service) +Visit : https://tracker.delivery + +### Self-Hosted (Self Service) +#### Setting Up the Development Environment +Delivery Tracker can be set up in local development environments and is also readily available for setup through GitHub Codespaces. + +[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/shlee322/delivery-tracker) + +Follow the instructions below for GitHub Codespaces: + +1. Click the "Open in GitHub Codespaces" button above to create a new Codespace. +2. Once in your Codespace terminal, enter `pnpm install` to install the necessary dependencies. +3. In GitHub Codespaces, navigate to the "Run and Debug" section from the sidebar and then click the "Run" button for "@delivery-tracker/server" to launch the server. +4. The service URL can be accessed via the Ports panel at the bottom of the GitHub Codespaces interface. + +#### Deploying Self-Hosted Services +See `Dockerfile` + +## Additional Information +### License +- Please read the `LICENSE` file. + +### Contact +- Please contact `contact@tracker.delivery` for more information. + +### Project Structure +- packages/api : GraphQL API +- packages/core : Scraper code +- packages/cli : A Command Line Interface (CLI) tool that uses the execute function from packages/api. +- packages/http : A self-hosted GraphQL HTTP server that uses the execute function from packages/api. diff --git a/package.json b/package.json new file mode 100644 index 0000000..c8c7040 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "engines": { + "node": ">=18", + "pnpm": ">=8" + }, + "devDependencies": { + "@types/node": "^18.16.0", + "@typescript-eslint/eslint-plugin": "^5.59.0", + "eslint": "^8.39.0", + "eslint-config-prettier": "^8.8.0", + "eslint-config-standard-with-typescript": "^34.0.1", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-n": "^15.7.0", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-promise": "^6.1.1", + "prettier": "^2.8.8", + "typescript": "^5.0.4" + } +} \ No newline at end of file diff --git a/packages/api/codegen.ts b/packages/api/codegen.ts new file mode 100644 index 0000000..dfc772d --- /dev/null +++ b/packages/api/codegen.ts @@ -0,0 +1,18 @@ +import type { CodegenConfig } from "@graphql-codegen/cli"; + +const config: CodegenConfig = { + schema: "src/schema/schema.graphql", + generates: { + "src/schema/generated/resolvers-types.ts": { + config: { + useIndexSignature: true, + scalars: { + DateTime: "string", + CarrierSpecificData: "Map", + }, + }, + plugins: ["typescript", "typescript-resolvers"], + }, + }, +}; +export default config; diff --git a/packages/api/package.json b/packages/api/package.json new file mode 100644 index 0000000..9547585 --- /dev/null +++ b/packages/api/package.json @@ -0,0 +1,23 @@ +{ + "name": "@delivery-tracker/api", + "private": true, + "type": "commonjs", + "exports": { + ".": "./dist/index.js" + }, + "types": "./dist/index.d.ts", + "scripts": { + "build": "graphql-codegen && tsc -p . && cp src/schema/schema.graphql dist/schema/schema.graphql", + "build-with-deps": "pnpm --filter '@delivery-tracker/core' build-with-deps && pnpm build" + }, + "dependencies": { + "@delivery-tracker/core": "workspace:*", + "graphql": "^16.6.0", + "winston": "^3.8.2" + }, + "devDependencies": { + "@graphql-codegen/cli": "^3.3.1", + "@graphql-codegen/typescript": "^3.0.4", + "@graphql-codegen/typescript-resolvers": "^3.2.1" + } +} \ No newline at end of file diff --git a/packages/api/src/AppContext.ts b/packages/api/src/AppContext.ts new file mode 100644 index 0000000..ff905a4 --- /dev/null +++ b/packages/api/src/AppContext.ts @@ -0,0 +1,7 @@ +import { type CarrierRegistry } from "@delivery-tracker/core"; + +interface AppContext { + carrierRegistry: CarrierRegistry; +} + +export type { AppContext }; diff --git a/packages/api/src/cursor/ArrayCursorConnection.ts b/packages/api/src/cursor/ArrayCursorConnection.ts new file mode 100644 index 0000000..2a586a2 --- /dev/null +++ b/packages/api/src/cursor/ArrayCursorConnection.ts @@ -0,0 +1,174 @@ +import { GraphQLError } from "graphql/error"; +import * as schema from "../schema/generated/resolvers-types"; + +interface Edge { + cursor: string; + node: Node | null; +} + +interface PageInfo { + hasNextPage: boolean; + hasPreviousPage: boolean; + startCursor: string | null; + endCursor: string | null; +} + +class ArrayCursorConnection { + private readonly isForward: boolean; + private readonly size: number; + private readonly cursor: string | null; + + constructor( + private readonly items: Node[], + private readonly limit: number, + private readonly first: number | null, + private readonly after: string | null, + private readonly last: number | null, + private readonly before: string | null + ) { + this.isForward = this.initIsForward(); + this.size = this.initSize(); + this.cursor = this.initCursor(); + } + + private initIsForward(): boolean { + if (this.first === null && this.last === null) { + throw new GraphQLError( + 'Invalid argument. Either the "first" or "last" parameter is required.', + { + extensions: { + code: schema.ErrorCode.BadRequest, + }, + } + ); + } + if (this.first !== null && this.last !== null) { + throw new GraphQLError( + 'Invalid argument. The "first" and "last" parameter cannot be used together.', + { + extensions: { + code: schema.ErrorCode.BadRequest, + }, + } + ); + } + + return this.first !== null; + } + + private initSize(): number { + const size = this.isForward ? this.first : this.last; + if (size === null) { + throw new Error( + "initSize or initIsForward function error. size is null." + ); + } + + if (size < 1) { + throw new GraphQLError( + `Invalid argument. The "${ + this.isForward ? "first" : "last" + }" parameter must be greater than 0.`, + { + extensions: { + code: schema.ErrorCode.BadRequest, + }, + } + ); + } + + if (size > this.limit) { + return this.limit; + } + + return size; + } + + private initCursor(): string | null { + const cursor = this.isForward ? this.after : this.before; + + if (this.isForward && this.before !== null) { + throw new GraphQLError( + 'The "before" parameter cannot be used with the "first" parameter.', + { + extensions: { + code: schema.ErrorCode.BadRequest, + }, + } + ); + } + if (!this.isForward && this.after !== null) { + throw new GraphQLError( + 'The "after" parameter cannot be used with the "last" parameter.', + { + extensions: { + code: schema.ErrorCode.BadRequest, + }, + } + ); + } + + return cursor; + } + + private sliceIndexInfo(): [number, number] { + const decodedCursor = this.decodeCursor(this.cursor); + const cursorIndex = + decodedCursor !== null + ? decodedCursor + : this.isForward + ? -1 + : this.items.length; + + if (this.isForward) { + return [cursorIndex + 1, cursorIndex + this.size]; + } else { + return [cursorIndex - this.size, cursorIndex - 1]; + } + } + + private encodeCursor(index: number): string { + return Buffer.from(JSON.stringify({ index }), "utf-8").toString("base64"); + } + + private decodeCursor(cursor: string | null): number | null { + if (cursor === null) { + return null; + } + return JSON.parse(Buffer.from(cursor, "base64").toString("utf-8")) + .index as number; + } + + edges(): Array> { + let [startIndex, endIndex] = this.sliceIndexInfo(); + startIndex = Math.max(startIndex, 0); + endIndex = Math.min(endIndex, this.items.length - 1); + + if (endIndex < startIndex) { + return []; + } + + const edges = []; + for (let i = startIndex; i <= endIndex; i++) { + edges.push({ + cursor: this.encodeCursor(i), + node: this.items[i], + }); + } + + return edges; + } + + pageInfo(): PageInfo { + const [startIndex, endIndex] = this.sliceIndexInfo(); + + return { + hasPreviousPage: startIndex > 0, + hasNextPage: endIndex < this.items.length - 1, + startCursor: this.encodeCursor(startIndex), + endCursor: this.encodeCursor(endIndex), + }; + } +} + +export { ArrayCursorConnection }; diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts new file mode 100644 index 0000000..f3b4e24 --- /dev/null +++ b/packages/api/src/index.ts @@ -0,0 +1,6 @@ +import { type AppContext } from "./AppContext"; +import { typeDefs } from "./typeDefs"; +import * as resolvers from "./resolvers"; +import { ErrorCode } from "./schema/generated/resolvers-types"; + +export { type AppContext, typeDefs, ErrorCode, resolvers }; diff --git a/packages/api/src/logger.ts b/packages/api/src/logger.ts new file mode 100644 index 0000000..e198261 --- /dev/null +++ b/packages/api/src/logger.ts @@ -0,0 +1,8 @@ +import type * as winston from "winston"; +import { logger as coreLogger } from "@delivery-tracker/core"; + +const apiRootLogger: winston.Logger = coreLogger.rootLogger.child({ + module: "api", +}); + +export { apiRootLogger }; diff --git a/packages/api/src/resolvers/carrier.ts b/packages/api/src/resolvers/carrier.ts new file mode 100644 index 0000000..ad177d8 --- /dev/null +++ b/packages/api/src/resolvers/carrier.ts @@ -0,0 +1,60 @@ +import { GraphQLError } from "graphql/error"; +import { type GraphQLResolveInfo } from "graphql/type"; +import { type AppContext } from "../AppContext"; +import * as schema from "../schema/generated/resolvers-types"; +import { ArrayCursorConnection } from "../cursor/ArrayCursorConnection"; +import { type Carrier } from "@delivery-tracker/core"; + +async function queryCarrierResolver( + parent: undefined, + args: schema.QueryCarrierArgs, + contextValue: { appContext: AppContext }, + info: GraphQLResolveInfo +): Promise { + const carrier = contextValue.appContext.carrierRegistry.get(args.id); + if (carrier == null) { + throw new GraphQLError("Carrier not found", { + extensions: { + code: schema.ErrorCode.NotFound, + }, + }); + } + + return carrier; +} + +async function queryCarriersResolver( + parent: undefined, + args: schema.QueryCarriersArgs, + contextValue: { appContext: AppContext }, + info: GraphQLResolveInfo +): Promise> { + return new ArrayCursorConnection( + contextValue.appContext.carrierRegistry.carriers, + 20, + args.first ?? null, + args.after ?? null, + args.last ?? null, + args.before ?? null + ); +} + +function carrierIdResolver( + parent: Carrier, + args: undefined, + contextValue: { appContext: AppContext }, + info: GraphQLResolveInfo +): string { + return parent.carrierId; +} + +const QueryResolvers = { + carrier: queryCarrierResolver, + carriers: queryCarriersResolver, +}; + +const CarrierResolvers = { + id: carrierIdResolver, +}; + +export { QueryResolvers, CarrierResolvers }; diff --git a/packages/api/src/resolvers/index.ts b/packages/api/src/resolvers/index.ts new file mode 100644 index 0000000..7306d98 --- /dev/null +++ b/packages/api/src/resolvers/index.ts @@ -0,0 +1,49 @@ +import { + QueryResolvers as CarrierQueryResolvers, + CarrierResolvers, +} from "./carrier"; +import { + ContactInfoResolvers, + LocationResolvers, + QueryResolvers as TrackQueryResolvers, + TrackEventResolvers, + TrackInfoResolvers, + TrackEventStatusResolvers, + type TrackInfoContext, + type TrackEventContext, + type ContactInfoContext, + type LocationContext, +} from "./track"; + +const resolvers = { + Query: { + ...CarrierQueryResolvers, + ...TrackQueryResolvers, + }, + Carrier: { + ...CarrierResolvers, + }, + TrackInfo: { + ...TrackInfoResolvers, + }, + TrackEvent: { + ...TrackEventResolvers, + }, + TrackEventStatus: { + ...TrackEventStatusResolvers, + }, + ContactInfo: { + ...ContactInfoResolvers, + }, + Location: { + ...LocationResolvers, + }, +}; + +export { + resolvers, + type TrackInfoContext, + type TrackEventContext, + type ContactInfoContext, + type LocationContext, +}; diff --git a/packages/api/src/resolvers/track.ts b/packages/api/src/resolvers/track.ts new file mode 100644 index 0000000..f1bec2e --- /dev/null +++ b/packages/api/src/resolvers/track.ts @@ -0,0 +1,413 @@ +import { GraphQLError } from "graphql/error"; +import { type GraphQLResolveInfo } from "graphql/type"; +import { apiRootLogger } from "../logger"; +import { + type TrackInfo, + type TrackEvent, + TrackEventStatusCode, + type ContactInfo, + type Location, + errors, +} from "@delivery-tracker/core"; +import { type AppContext } from "../AppContext"; +import * as schema from "../schema/generated/resolvers-types"; +import { type ContactInfoPhoneNumberArgs } from "../schema/generated/resolvers-types"; +import { ArrayCursorConnection } from "../cursor/ArrayCursorConnection"; + +interface TrackInfoContext { + trackingNumber: string; + trackInfo: TrackInfo; +} + +interface TrackEventContext { + trackInfoContext: TrackInfoContext; + trackEvent: TrackEvent; +} + +interface ContactInfoContext { + trackInfoContext: TrackInfoContext; + self: ContactInfo; +} + +interface LocationContext { + trackInfoContext: TrackInfoContext; + self: Location; +} + +async function queryTrackResolver( + parent: undefined, + args: schema.QueryTrackArgs, + contextValue: { appContext: AppContext }, + info: GraphQLResolveInfo +): Promise { + const carrier = contextValue.appContext.carrierRegistry.get(args.carrierId); + if (carrier == null) { + throw new GraphQLError("Carrier not found", { + extensions: { + code: schema.ErrorCode.NotFound, + }, + }); + } + + let trackInfo = null; + try { + trackInfo = await carrier.track({ + trackingNumber: args.trackingNumber, + // @ts-expect-error + context: contextValue, + }); + } catch (e) { + if ( + e instanceof errors.TrackError && + !(e instanceof errors.InternalError) + ) { + throw new GraphQLError(e.message ?? e.code, { + originalError: e, + extensions: { + code: e.code, + }, + }); + } + + apiRootLogger.error("track error", { + carrierId: carrier.carrierId, + trackingNumber: args.trackingNumber, + error: e, + }); + + if (e instanceof Error) { + throw new GraphQLError("Please try again in a few minutes.", { + originalError: e, + extensions: { + code: schema.ErrorCode.Internal, + }, + }); + } else { + throw new GraphQLError("Please try again in a few minutes.", { + extensions: { + code: schema.ErrorCode.Internal, + }, + }); + } + } + + return { + trackingNumber: args.trackingNumber, + trackInfo, + }; +} + +function trackInfoTrackingNumberResolver( + parent: TrackInfoContext, + args: undefined, + contextValue: undefined, + info: GraphQLResolveInfo +): string { + return parent.trackingNumber; +} + +function trackInfoLastEventResolver( + parent: TrackInfoContext, + args: undefined, + contextValue: undefined, + info: GraphQLResolveInfo +): TrackEventContext | null { + const reversedEvents = [...parent.trackInfo.events].reverse(); + const deliveredEvent = + reversedEvents.find((item) => { + return item.status.code === TrackEventStatusCode.Delivered; + }) ?? null; + + if (deliveredEvent !== null) { + return { + trackInfoContext: parent, + trackEvent: deliveredEvent, + }; + } + + return { + trackInfoContext: parent, + trackEvent: parent.trackInfo.events[parent.trackInfo.events.length - 1], + }; +} + +function trackInfoEventsResolver( + parent: TrackInfoContext, + args: schema.TrackInfoEventsArgs, + contextValue: undefined, + info: GraphQLResolveInfo +): ArrayCursorConnection { + const events = parent.trackInfo.events.map((trackEvent) => ({ + trackInfoContext: parent, + trackEvent, + })); + return new ArrayCursorConnection( + events, + 20, + args.first ?? null, + args.after ?? null, + args.last ?? null, + args.before ?? null + ); +} + +function trackInfoSenderResolver( + parent: TrackInfoContext, + args: undefined, + contextValue: undefined, + info: GraphQLResolveInfo +): ContactInfoContext | null { + if (parent.trackInfo.sender === null) { + return null; + } + + return { + trackInfoContext: parent, + self: parent.trackInfo.sender, + }; +} + +function trackInfoRecipientResolver( + parent: TrackInfoContext, + args: undefined, + contextValue: undefined, + info: GraphQLResolveInfo +): ContactInfoContext | null { + if (parent.trackInfo.recipient === null) { + return null; + } + + return { + trackInfoContext: parent, + self: parent.trackInfo.recipient, + }; +} + +function trackEventStatusResolver( + parent: TrackEventContext, + args: undefined, + contextValue: undefined, + info: GraphQLResolveInfo +): TrackEventContext { + return parent; +} + +function trackEventTimeResolver( + parent: TrackEventContext, + args: undefined, + contextValue: undefined, + info: GraphQLResolveInfo +): schema.Scalars["DateTime"] | null { + return parent.trackEvent.time?.toISO() ?? null; +} + +function trackEventLocationResolver( + parent: TrackEventContext, + args: undefined, + contextValue: undefined, + info: GraphQLResolveInfo +): LocationContext | null { + if (parent.trackEvent.location === null) { + return null; + } + + return { + trackInfoContext: parent.trackInfoContext, + self: parent.trackEvent.location, + }; +} + +function trackEventContactResolver( + parent: TrackEventContext, + args: undefined, + contextValue: undefined, + info: GraphQLResolveInfo +): ContactInfoContext | null { + if (parent.trackEvent.contact === null) { + return null; + } + + return { + trackInfoContext: parent.trackInfoContext, + self: parent.trackEvent.contact, + }; +} + +function trackEventDescriptionResolver( + parent: TrackEventContext, + args: undefined, + contextValue: undefined, + info: GraphQLResolveInfo +): string | null { + return parent.trackEvent.description; +} + +function trackEventStatusCodeResolver( + parent: TrackEventContext, + args: undefined, + contextValue: undefined, + info: GraphQLResolveInfo +): schema.TrackEventStatusCode { + switch (parent.trackEvent.status.code) { + case TrackEventStatusCode.Unknown: + return schema.TrackEventStatusCode.Unknown; + case TrackEventStatusCode.InformationReceived: + return schema.TrackEventStatusCode.InformationReceived; + case TrackEventStatusCode.AtPickup: + return schema.TrackEventStatusCode.AtPickup; + case TrackEventStatusCode.InTransit: + return schema.TrackEventStatusCode.InTransit; + case TrackEventStatusCode.OutForDelivery: + return schema.TrackEventStatusCode.OutForDelivery; + case TrackEventStatusCode.AttemptFail: + return schema.TrackEventStatusCode.AttemptFail; + case TrackEventStatusCode.Delivered: + return schema.TrackEventStatusCode.Delivered; + case TrackEventStatusCode.AvailableForPickup: + return schema.TrackEventStatusCode.AvailableForPickup; + case TrackEventStatusCode.Exception: + return schema.TrackEventStatusCode.Exception; + } + + apiRootLogger.warn("Unknown TrackEventStatusCode", { + trackingNumber: parent.trackInfoContext.trackingNumber, + statusCode: parent.trackEvent.status.code, + }); + + return schema.TrackEventStatusCode.Unknown; +} + +function trackEventStatusNameResolver( + parent: TrackEventContext, + args: undefined, + contextValue: undefined, + info: GraphQLResolveInfo +): string | null { + return parent.trackEvent.status.name; +} + +function contactInfoNameResolver( + parent: ContactInfoContext, + args: undefined, + contextValue: undefined, + info: GraphQLResolveInfo +): string | null { + return parent.self.name; +} + +function contactInfoLocationResolver( + parent: ContactInfoContext, + args: undefined, + contextValue: undefined, + info: GraphQLResolveInfo +): LocationContext | null { + if (parent.self.location === null) { + return null; + } + + return { + trackInfoContext: parent.trackInfoContext, + self: parent.self.location, + }; +} + +function contactInfoPhoneNumberResolver( + parent: ContactInfoContext, + args: ContactInfoPhoneNumberArgs, + contextValue: undefined, + info: GraphQLResolveInfo +): string | null { + if (parent.self.phoneNumber === null) { + return null; + } + + if ( + typeof parent.self.phoneNumber === "object" && + "maskedPhoneNumber" in parent.self.phoneNumber + ) { + if (args.allowInvalidFormat !== false) { + return null; + } + + return parent.self.phoneNumber.maskedPhoneNumber; + } + + return parent.self.phoneNumber.number; +} + +function locationCountryCodeResolver( + parent: LocationContext, + args: undefined, + contextValue: undefined, + info: GraphQLResolveInfo +): string | null { + return parent.self.countryCode; +} + +function locationPostalCodeResolver( + parent: LocationContext, + args: undefined, + contextValue: undefined, + info: GraphQLResolveInfo +): string | null { + return parent.self.postalCode; +} + +function locationNameResolver( + parent: LocationContext, + args: undefined, + contextValue: undefined, + info: GraphQLResolveInfo +): string | null { + return parent.self.name; +} + +const QueryResolvers = { + track: queryTrackResolver, +}; + +const TrackInfoResolvers = { + trackingNumber: trackInfoTrackingNumberResolver, + lastEvent: trackInfoLastEventResolver, + events: trackInfoEventsResolver, + sender: trackInfoSenderResolver, + recipient: trackInfoRecipientResolver, +}; + +const TrackEventResolvers = { + status: trackEventStatusResolver, + time: trackEventTimeResolver, + location: trackEventLocationResolver, + contact: trackEventContactResolver, + description: trackEventDescriptionResolver, +}; + +const TrackEventStatusResolvers = { + code: trackEventStatusCodeResolver, + name: trackEventStatusNameResolver, +}; + +const ContactInfoResolvers = { + name: contactInfoNameResolver, + location: contactInfoLocationResolver, + phoneNumber: contactInfoPhoneNumberResolver, +}; + +const LocationResolvers = { + countryCode: locationCountryCodeResolver, + postalCode: locationPostalCodeResolver, + name: locationNameResolver, +}; + +export { + QueryResolvers, + TrackInfoResolvers, + TrackEventResolvers, + TrackEventStatusResolvers, + ContactInfoResolvers, + LocationResolvers, + type TrackInfoContext, + type TrackEventContext, + type ContactInfoContext, + type LocationContext, +}; diff --git a/packages/api/src/schema/schema.graphql b/packages/api/src/schema/schema.graphql new file mode 100644 index 0000000..abaff4e --- /dev/null +++ b/packages/api/src/schema/schema.graphql @@ -0,0 +1,349 @@ +""" +A scalar type representing date and time in a standardized format. +DateTime follows the RFC3339 (ISO8601) standard and includes a timezone. +The timezone may vary depending on the location of the shipment event. +""" +scalar DateTime + +""" +This GraphQL schema defines a shipment tracking system that track shipments, fetch carrier information, and obtain detailed tracking event data. +""" +type Query { + """ + Retrieves tracking information for a shipment based on the carrier ID and tracking number. + + See: https://tracker.delivery/docs/tracking-api + """ + track( + """ + The unique identifier of the carrier. + """ + carrierId: ID!, + + """ + The tracking number of the shipment. + """ + trackingNumber: String! + ): TrackInfo + + """ + Returns a connection object containing carriers. + + See: https://tracker.delivery/docs/tracking-api + """ + carriers( + """ + Relay-style Cursor Connection `first` argument. + """ + first: Int, + """ + Relay-style Cursor Connection `after` argument. + """ + after: String, + """ + Relay-style Cursor Connection `last` argument. + """ + last: Int, + """ + Relay-style Cursor Connection `before` argument. + """ + before: String + ): CarrierConnection + + """ + Returns the Carrier object with the Carrier ID. + + See: https://tracker.delivery/docs/tracking-api + """ + carrier( + """ + The unique identifier of the carrier. + """ + id: ID! + ): Carrier +} + +""" +Represents a shipping carrier. +""" +type Carrier { + id: ID! +} + +""" +A connection object for a list of shipping carriers. +""" +type CarrierConnection { + """ + A list of edges in the connection, each containing a Carrier node. + """ + edges: [CarrierEdge] + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An edge in the CarrierConnection, containing a Carrier node. +""" +type CarrierEdge { + """ + The Carrier node in the edge. + """ + node: Carrier + + """ + A cursor for use in pagination. + """ + cursor: String! +} + +""" +Contains tracking information for a shipment. +""" +type TrackInfo { + """ + The tracking number for the shipment. + """ + trackingNumber: String! + + """ + Represents the most likely event that has occurred recently. + This may differ from the last item in `TrackInfo.events`. + + For example, if there are any abnormal situations such as OUT_FOR_DELIVERY events being registered after the DELIVERED event in events, the lastEvent.status.code will still prioritize DELIVERED. + """ + lastEvent: TrackEvent + + """ + A list of tracking events for the shipment. + + The event sort order is from oldest to newest events. + + Important: See also `TrackInfo.lastEvent` field. + """ + events( + """ + Relay-style Cursor Connection `first` argument. + """ + first: Int, + """ + Relay-style Cursor Connection `after` argument. + """ + after: String, + """ + Relay-style Cursor Connection `last` argument. + """ + last: Int, + """ + Relay-style Cursor Connection `before` argument. + """ + before: String + ): TrackEventConnection + + """ + The sender's contact information. + """ + sender: ContactInfo + + """ + The recipient's contact information. + """ + recipient: ContactInfo +} + +""" +Represents a single tracking event for a shipment. +""" +type TrackEvent { + """ + The status of the tracking event. + """ + status: TrackEventStatus! + + """ + The time of the tracking event. + """ + time: DateTime + + """ + The location of the tracking event. + """ + location: Location + + """ + The contact information related to the tracking event. + """ + contact: ContactInfo + + """ + The description of the tracking event. + """ + description: String +} + +""" +A connection object for a list of tracking events. +""" +type TrackEventConnection { + """ + A list of edges in the connection, each containing a TrackEvent node. + """ + edges: [TrackEventEdge] + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +Represents the status of a tracking event. +""" +type TrackEventStatus { + """ + The general status of the tracking event. + """ + code: TrackEventStatusCode! + + """ + The name of the tracking event. + """ + name: String +} + +""" +An edge in the TrackEventConnection, containing a TrackEvent node. +""" +type TrackEventEdge { + """ + The TrackEvent node in the edge. + """ + node: TrackEvent + + """ + A cursor for use in pagination. + """ + cursor: String! +} + +""" +An enumeration of possible general statuses for a tracking event. +""" +enum TrackEventStatusCode { + "The tracking status is currently unknown or could not be determined." + UNKNOWN + "The tracking information has been received and is being processed." + INFORMATION_RECEIVED + "The shipment or package is currently at the pickup location." + AT_PICKUP + "The shipment is in transit and is on its way to the destination." + IN_TRANSIT + "The shipment is out for delivery and is on its way to the recipient." + OUT_FOR_DELIVERY + "Delivery attempt has failed, and the package is still not delivered." + ATTEMPT_FAIL + "The shipment has been successfully delivered to the recipient." + DELIVERED + "The package is available for pickup at a designated location." + AVAILABLE_FOR_PICKUP + "There is an exception or issue with the shipment that requires attention." + EXCEPTION +} + +""" +Represents the location of a tracking event or contact. +""" +type Location { + """ + The country code for the location. + """ + countryCode: String + + """ + The postal code for the location. + """ + postalCode: String + + """ + The name of the location. + """ + name: String +} + +""" +Represents contact information for a sender, recipient, or tracking event. +""" +type ContactInfo { + """ + The name of the contact. + """ + name: String + + """ + The location of the contact. + """ + location: Location + + """ + The phone number of the contact. + + It follows the E.164 format by default, but if allowInvalidFormat is true and masking areas is detected, it will be displayed as X. + If allowInvalidFormat is false, null will be returned if the phone number is not in the correct format. + """ + phoneNumber( + """ + If true, masking areas will be represented by X. + If false and detected masking areas, field will return null. + + default: true + """ + allowInvalidFormat: Boolean + ): String +} + +""" +Represents pagination information for connections following the Relay Cursor Connection Specification. +Indicates if there are more items when paginating forwards or backwards and provides cursors for pagination. +""" +type PageInfo { + """ + Indicates if there are more items when paginating forwards. + """ + hasNextPage: Boolean! + + """ + Indicates if there are more items when paginating backwards. + """ + hasPreviousPage: Boolean! + + """ + The cursor for the first item in the current page, used for paginating backwards. + """ + startCursor: String + + """ + The cursor for the last item in the current page, used for paginating forwards. + """ + endCursor: String +} + +""" +An enumeration of error codes that could be returned by the API, providing insight into what kind of error occurred. + +See: https://tracker.delivery/docs/error-handling +""" +enum ErrorCode { + """ + An unexpected internal error occurred within the server. This could be due to various reasons such as server misconfiguration, operational failures, or other unexpected conditions. + """ + INTERNAL + """ + The request could not be understood by the server due to malformed syntax. The client should not repeat the request without modifications. + """ + BAD_REQUEST + """ + The requested resource could not be found. Subsequent requests by the client are permissible. + """ + NOT_FOUND +} diff --git a/packages/api/src/typeDefs.ts b/packages/api/src/typeDefs.ts new file mode 100644 index 0000000..ba2aab5 --- /dev/null +++ b/packages/api/src/typeDefs.ts @@ -0,0 +1,9 @@ +import { readFileSync } from "fs"; +import path from "path"; + +const typeDefs = readFileSync( + path.resolve(__dirname, "./schema/schema.graphql"), + "utf8" +); + +export { typeDefs }; diff --git a/packages/api/tsconfig.json b/packages/api/tsconfig.json new file mode 100644 index 0000000..57edf2b --- /dev/null +++ b/packages/api/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.base", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist" + }, + "exclude": [ + "codegen.ts", + "dist/**/*" + ] +} \ No newline at end of file diff --git a/packages/cli/package.json b/packages/cli/package.json new file mode 100644 index 0000000..f3b4315 --- /dev/null +++ b/packages/cli/package.json @@ -0,0 +1,25 @@ +{ + "name": "@delivery-tracker/cli", + "private": true, + "type": "commonjs", + "scripts": { + "build": "tsc -p .", + "start": "npx node dist/index.js", + "build-with-deps": "pnpm --filter '@delivery-tracker/api' build-with-deps && pnpm build" + }, + "bin": { + "delivery-tracker": "./dist/index.js" + }, + "dependencies": { + "@delivery-tracker/api": "workspace:*", + "@delivery-tracker/core": "workspace:*", + "graphql": "^16.6.0", + "luxon": "^3.4.0", + "winston": "^3.8.2", + "yargs": "^17.7.1" + }, + "devDependencies": { + "@types/luxon": "^3.3.1", + "@types/yargs": "^17.0.24" + } +} \ No newline at end of file diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts new file mode 100644 index 0000000..732e666 --- /dev/null +++ b/packages/cli/src/index.ts @@ -0,0 +1,146 @@ +#!/usr/bin/env node +import yargs from "yargs/yargs"; +import { hideBin } from "yargs/helpers"; +import * as graphql from "graphql"; +import { + DefaultCarrierRegistry, + logger as coreLogger, +} from "@delivery-tracker/core"; +import { typeDefs, resolvers, type AppContext } from "@delivery-tracker/api"; +import { initLogger } from "./logger"; +import { type Maybe } from "graphql/jsutils/Maybe"; +import { type ExecutionResult } from "graphql/execution/execute"; + +const cliLogger = coreLogger.rootLogger.child({ module: "cli" }); + +interface TrackArguments { + carrierId: string; + trackingNumber: string; +} + +async function executeGraphQL( + source: string, + variableValues?: Maybe>> +): Promise { + const schema = graphql.buildSchema(typeDefs); + const carrierRegistry = new DefaultCarrierRegistry(); + await carrierRegistry.init(); + const appContext: AppContext = { + carrierRegistry, + }; + return await graphql.graphql({ + schema, + source, + rootValue: resolvers.resolvers.Query, + contextValue: { + appContext, + }, + variableValues, + }); +} + +initLogger(); + +// TODO : 개발중인 기능으로 수정 필요 +yargs(hideBin(process.argv)) + .usage("$0 [args]") + .command( + "track ", + "Track a shipping", + (args) => { + return args + .positional("carrier-id", { + describe: "Carrier ID", + }) + .positional("tracking-number", { + describe: "Tracking Number", + }); + }, + async (args) => { + const result = await executeGraphQL( + ` + query Track($carrierId: ID!, $trackingNumber: String!) { + track(carrierId: $carrierId, trackingNumber: $trackingNumber) { + trackingNumber + lastEvent { + status { + code + name + } + time + location { + countryCode + postalCode + name + } + contact { + name + phoneNumber + } + description + } + events(last: 20) { + edges { + node { + status { + code + name + } + time + location { + countryCode + postalCode + name + } + contact { + name + phoneNumber + } + description + } + } + } + sender { + name + phoneNumber + } + recipient { + name + phoneNumber + } + } + } + `, + { + carrierId: args.carrierId.toString(), + trackingNumber: args.trackingNumber.toString(), + } + ); + cliLogger.info("result", { result }); + } + ) + .command( + "carriers", + "Show Supported Carriers", + (args) => {}, + async (args) => { + const result = await executeGraphQL( + ` + query Carriers { + carriers(first: 20) { + edges { + node { + id + } + } + } + } + `, + {} + ); + cliLogger.info("result", { result }); + } + ) + .help() + .demandCommand(1) + .parse(); diff --git a/packages/cli/src/logger.ts b/packages/cli/src/logger.ts new file mode 100644 index 0000000..1b468e5 --- /dev/null +++ b/packages/cli/src/logger.ts @@ -0,0 +1,36 @@ +import * as winston from "winston"; +import { logger as coreLogger } from "@delivery-tracker/core"; +import { DateTime } from "luxon"; + +function initLogger(): void { + coreLogger.rootLogger.format = winston.format.combine( + winston.format.json({ + replacer: (key, value) => { + // luxon + if (value instanceof DateTime) { + return value.toISO(); + } + + // libphonenumber-js / PhoneNumber + if ( + typeof value === "object" && + value !== null && + "number" in value && + typeof value.number === "string" + ) { + return value.number; + } + return value; + }, + }) + ); + + if (process.env.LOG_LEVEL) { + coreLogger.rootLogger.level = process.env.LOG_LEVEL; + } + + // TODO : file mode + coreLogger.rootLogger.add(new winston.transports.Console()); +} + +export { initLogger }; diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json new file mode 100644 index 0000000..f6a9e8f --- /dev/null +++ b/packages/cli/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist" + }, +} \ No newline at end of file diff --git a/packages/core/jest.config.js b/packages/core/jest.config.js new file mode 100644 index 0000000..b413e10 --- /dev/null +++ b/packages/core/jest.config.js @@ -0,0 +1,5 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', +}; \ No newline at end of file diff --git a/packages/core/package.json b/packages/core/package.json new file mode 100644 index 0000000..31ad3cd --- /dev/null +++ b/packages/core/package.json @@ -0,0 +1,35 @@ +{ + "name": "@delivery-tracker/core", + "private": true, + "type": "commonjs", + "exports": { + ".": "./dist/index.js" + }, + "types": "./dist/index.d.ts", + "scripts": { + "build": "tsc -p .", + "build-with-deps": "pnpm build", + "test": "jest" + }, + "dependencies": { + "cheerio": "1.0.0-rc.12", + "iconv-lite": "^0.6.3", + "jsdom": "^21.1.1", + "libphonenumber-js": "^1.10.28", + "luxon": "^3.4.0", + "tough-cookie": "^4.1.2", + "winston": "^3.8.2", + "yaml": "^2.3.1", + "zod": "^3.21.4" + }, + "devDependencies": { + "@jest/globals": "^29.6.2", + "@types/jsdom": "^21.1.1", + "@types/luxon": "^3.3.1", + "@types/tough-cookie": "^4.0.2", + "jest": "^29.6.2", + "ts-jest": "^29.1.1", + "ts-node": "^10.9.1", + "typescript": "^5.1.6" + } +} \ No newline at end of file diff --git a/packages/core/src/CarrierAlias.ts b/packages/core/src/CarrierAlias.ts new file mode 100644 index 0000000..24269e5 --- /dev/null +++ b/packages/core/src/CarrierAlias.ts @@ -0,0 +1,27 @@ +import { + type CarrierTrackInput, + Carrier, + type TrackInfo, + type CarrierInitInput, +} from "./core"; + +class CarrierAlias extends Carrier { + constructor(readonly alias: string, readonly carrier: Carrier) { + super(); + } + + public async init(input: CarrierInitInput & any): Promise { + await super.init(input); + await this.carrier.init(input); + } + + get carrierId(): string { + return this.alias; + } + + async track(input: CarrierTrackInput): Promise { + return await this.carrier.track(input); + } +} + +export { CarrierAlias }; diff --git a/packages/core/src/carrier-registry/CarrierRegistry.ts b/packages/core/src/carrier-registry/CarrierRegistry.ts new file mode 100644 index 0000000..0af0a6c --- /dev/null +++ b/packages/core/src/carrier-registry/CarrierRegistry.ts @@ -0,0 +1,8 @@ +import { type Carrier } from "../core/interfaces"; + +interface CarrierRegistry { + get: (carrierId: string) => Carrier | null; + get carriers(): Carrier[]; +} + +export { type CarrierRegistry }; diff --git a/packages/core/src/carrier-registry/DefaultCarrierRegistry.ts b/packages/core/src/carrier-registry/DefaultCarrierRegistry.ts new file mode 100644 index 0000000..e882faa --- /dev/null +++ b/packages/core/src/carrier-registry/DefaultCarrierRegistry.ts @@ -0,0 +1,159 @@ +import fsPromises from "fs/promises"; +import YAML from "yaml"; +import { type CarrierRegistry } from "./CarrierRegistry"; +import { type Carrier } from "../core/interfaces"; +import { CarrierUpstreamFetcher } from "../carrier-upstream-fetcher"; +import { CJLogistics } from "../carriers/kr.cjlogistics"; +import { Chunilps } from "../carriers/kr.chunilps"; +import { CarrierAlias } from "../CarrierAlias"; +import { CVSnet } from "../carriers/kr.cvsnet"; +import { DHL } from "../carriers/de.dhl"; +import { Cway } from "../carriers/kr.cway"; +import { Sagawa } from "../carriers/jp.sagawa"; +import { Daesin } from "../carriers/kr.daesin"; +import { Hanjin } from "../carriers/kr.hanjin"; +import { KoreaPost } from "../carriers/kr.epost"; +import { HonamLogis } from "../carriers/kr.honamlogis"; +import { IlyangLogis } from "../carriers/kr.ilyanglogis"; +import { KyungdongExpress } from "../carriers/kr.kdexp"; +import { Kunyoung } from "../carriers/kr.kunyoung"; +import { Logen } from "../carriers/kr.logen"; +import { LotteGlobalLogistics } from "../carriers/kr.lotte"; +import { SLX } from "../carriers/kr.slx"; +import { TodayPickup } from "../carriers/kr.todaypickup"; +import { EMS } from "../carriers/un.upu.ems"; +import { TNT } from "../carriers/nl.tnt"; +import { Fedex } from "../carriers/us.fedex"; +import { UPS } from "../carriers/us.ups"; +import { USPS } from "../carriers/us.usps"; + +interface DefaultCarrierRegistryConfig { + carriers: Record< + string, + { + enabled: boolean; + } + >; +} + +class DefaultCarrierRegistry implements CarrierRegistry { + private readonly _carriers = new Map(); + private _config: DefaultCarrierRegistryConfig = this.defaultConfig(); + + public async init(): Promise { + await this.loadConfig(); + + await this.register(new DHL()); + await this.register(new Sagawa()); + await this.register(new CJLogistics()); + await this.register(new CarrierAlias("kr.cupost", new CJLogistics())); + await this.register(new Chunilps()); + await this.register(new CVSnet()); + await this.register(new Cway()); + await this.register(new Daesin()); + await this.register(new KoreaPost()); + await this.register(new CarrierAlias("kr.homepick", new Hanjin())); + await this.register(new Hanjin()); + await this.register(new HonamLogis()); + await this.register(new IlyangLogis()); + await this.register(new KyungdongExpress()); + await this.register(new Kunyoung()); + await this.register(new Logen()); + await this.register(new LotteGlobalLogistics()); + await this.register(new SLX()); + await this.register(new CarrierAlias("kr.swgexp.epost", new KoreaPost())); + await this.register( + new CarrierAlias("kr.swgexp.cjlogistics", new CJLogistics()) + ); + await this.register(new TodayPickup()); + await this.register(new TNT()); + await this.register(new EMS()); + await this.register(new Fedex()); + await this.register(new UPS()); + await this.register(new USPS()); + } + + private defaultConfig(): DefaultCarrierRegistryConfig { + return { + carriers: { + "de.dhl": { + enabled: false, + }, + "us.fedex": { + enabled: false, + }, + "us.usps": { + enabled: false, + }, + }, + }; + } + + private async loadConfig(): Promise { + const configFilePath = + process.env.DELIVERY_TRACKER_CARRIER_REGISTRY_CONFIG_FILE; + if (configFilePath === undefined) { + this._config = this.defaultConfig(); + return; + } + + const configFileRawText = await fsPromises.readFile(configFilePath, { + encoding: "utf-8", + }); + // TODO zod + this._config = YAML.parse(configFileRawText); + } + + protected getConfig(carrier: Carrier): any { + const defaultConfig = this.defaultConfig(); + const carrierConfig = this._config.carriers[carrier.carrierId]; + const enabled = + carrierConfig?.enabled ?? + defaultConfig.carriers[carrier.carrierId]?.enabled ?? + true; + + if (!enabled) { + return { + enabled: false, + }; + } + + return { + ...carrierConfig, + }; + } + + protected createCarrierUpstreamFetcher( + carrier: Carrier + ): CarrierUpstreamFetcher { + return new CarrierUpstreamFetcher({ carrier }); + } + + private async register(carrier: Carrier): Promise { + const config = this.getConfig(carrier); + if (config.enabled === false) { + return; + } + + await carrier.init({ + upstreamFetcher: this.createCarrierUpstreamFetcher(carrier), + config, + }); + + this.add(carrier); + } + + private add(carrier: Carrier): void { + this._carriers.set(carrier.carrierId, carrier); + } + + get(carrierId: string): Carrier | null { + return this._carriers.get(carrierId) ?? null; + } + + get carriers(): Carrier[] { + return Array.from(this._carriers.values()); + } +} + +export { DefaultCarrierRegistry }; diff --git a/packages/core/src/carrier-registry/index.ts b/packages/core/src/carrier-registry/index.ts new file mode 100644 index 0000000..8dab468 --- /dev/null +++ b/packages/core/src/carrier-registry/index.ts @@ -0,0 +1,2 @@ +export type { CarrierRegistry } from "./CarrierRegistry"; +export { DefaultCarrierRegistry } from "./DefaultCarrierRegistry"; diff --git a/packages/core/src/carrier-upstream-fetcher/CarrierUpstreamFetcher.ts b/packages/core/src/carrier-upstream-fetcher/CarrierUpstreamFetcher.ts new file mode 100644 index 0000000..59fce96 --- /dev/null +++ b/packages/core/src/carrier-upstream-fetcher/CarrierUpstreamFetcher.ts @@ -0,0 +1,22 @@ +import { type Carrier } from "../core"; + +interface CarrierUpstreamFetcherInitInput { + carrier: Carrier; +} + +class CarrierUpstreamFetcher { + protected readonly carrier: Carrier; + + constructor(input: CarrierUpstreamFetcherInitInput) { + this.carrier = input.carrier; + } + + public async fetch( + input: RequestInfo | URL, + init?: RequestInit + ): Promise { + return await fetch(input, init); + } +} + +export { type CarrierUpstreamFetcherInitInput, CarrierUpstreamFetcher }; diff --git a/packages/core/src/carrier-upstream-fetcher/index.ts b/packages/core/src/carrier-upstream-fetcher/index.ts new file mode 100644 index 0000000..870c0d9 --- /dev/null +++ b/packages/core/src/carrier-upstream-fetcher/index.ts @@ -0,0 +1,4 @@ +export { + type CarrierUpstreamFetcherInitInput, + CarrierUpstreamFetcher, +} from "./CarrierUpstreamFetcher"; diff --git a/packages/core/src/carriers/de.dhl/DHLAPISchemas.ts b/packages/core/src/carriers/de.dhl/DHLAPISchemas.ts new file mode 100644 index 0000000..2b2a1fe --- /dev/null +++ b/packages/core/src/carriers/de.dhl/DHLAPISchemas.ts @@ -0,0 +1,243 @@ +/** + * This file was automatically generated by the openapi-zod-client. + * API Spec: https://developer.dhl.com/api-reference/shipment-tracking + */ + +import { z } from "zod"; + +const supermodelIoGs1PropertiesCountryCode = z.string(); +const supermodelIoLogisticsSupportingPlace = z + .object({ + address: z + .object({ + countryCode: supermodelIoGs1PropertiesCountryCode, + postalCode: z.string(), + addressLocality: z.string(), + streetAddress: z.string(), + }) + .partial() + .passthrough(), + }) + .partial() + .passthrough(); +const supermodelIoSchemaorgDataType = z.unknown(); +const supermodelIoSchemaorgDateTime = supermodelIoSchemaorgDataType; +const supermodelIoSchemaorgDate = supermodelIoSchemaorgDataType; +const supermodelIoLogisticsSupportingTimestamp = + supermodelIoSchemaorgDateTime.and(supermodelIoSchemaorgDate); +const supermodelIoLogisticsTrackingShipmentEvent = z + .object({ + timestamp: supermodelIoLogisticsSupportingTimestamp, + location: supermodelIoLogisticsSupportingPlace, + statusCode: z.enum([ + "pre-transit", + "transit", + "delivered", + "failure", + "unknown", + ]), + status: z.string(), + description: z.string(), + pieceIds: z.array(z.string()), + remark: z.string(), + nextSteps: z.string(), + }) + .partial() + .passthrough(); +const supermodelIoLogisticsSupportingOrganization = z + .object({ organizationName: z.string() }) + .partial() + .passthrough(); +const supermodelIoLogisticsSupportingProduct = z + .object({ productName: z.string() }) + .partial() + .passthrough(); +const supermodelIoLogisticsSupportingProvider = z + .object({ + destinationProvider: z.enum([ + "oepag", + "express", + "parcel-bl", + "bpost", + "rapido", + "acs-courier", + "parcel-cz", + "freight", + "parcel-de", + "trans-o-flex", + "bring", + "parcel-es", + "posti", + "relais-colis", + "colis-prive", + "freight-fr", + "chronopost", + "parcel-uk", + "hrvatska-posta", + "magyar-posta", + "anpost", + "fastway", + "parcel-lu", + "parcel-be", + "parcel-nl", + "posta", + "slovak-parcel-service", + "parcel-pl", + "parcel-pt", + "urgent-cargus", + ]), + }) + .partial() + .passthrough(); +const supermodelIoLogisticsSupportingPerson = z + .object({ familyName: z.string(), givenName: z.string(), name: z.string() }) + .partial() + .passthrough(); +const supermodelIoLogisticsTrackingProofOfDelivery = z + .object({ + timestamp: supermodelIoLogisticsSupportingTimestamp, + signatureUrl: z.string(), + documentUrl: z.string(), + signed: supermodelIoLogisticsSupportingPerson, + }) + .partial() + .passthrough(); +const supermodelIoLogisticsTrackingDgfLocation = z + .object({ "dgf:locationName": z.string() }) + .partial() + .passthrough(); +const supermodelIoLogisticsTrackingDgfAirport = + supermodelIoLogisticsTrackingDgfLocation; +const supermodelIoLogisticsTrackingDgfRoute = z + .object({ + "dgf:vesselName": z.string(), + "dgf:voyageFlightNumber": z.string(), + "dgf:airportOfDeparture": supermodelIoLogisticsTrackingDgfAirport, + "dgf:airportOfDestination": supermodelIoLogisticsTrackingDgfAirport, + "dgf:estimatedDepartureDate": supermodelIoLogisticsSupportingTimestamp, + "dgf:estimatedArrivalDate": supermodelIoLogisticsSupportingTimestamp, + "dgf:placeOfAcceptance": supermodelIoLogisticsTrackingDgfLocation, + "dgf:portOfLoading": supermodelIoLogisticsTrackingDgfLocation, + "dgf:portOfUnloading": supermodelIoLogisticsTrackingDgfLocation, + "dgf:placeOfDelivery": supermodelIoLogisticsTrackingDgfLocation, + }) + .partial() + .passthrough(); +const supermodelIoLogisticsTrackingShipmentDetails = z + .object({ + carrier: supermodelIoLogisticsSupportingOrganization, + product: supermodelIoLogisticsSupportingProduct, + provider: supermodelIoLogisticsSupportingProvider, + receiver: supermodelIoLogisticsSupportingOrganization.and( + supermodelIoLogisticsSupportingPerson + ), + sender: supermodelIoLogisticsSupportingOrganization.and( + supermodelIoLogisticsSupportingPerson + ), + proofOfDelivery: supermodelIoLogisticsTrackingProofOfDelivery, + totalNumberOfPieces: z.number(), + pieceIds: z.array(z.string()), + weight: z.object({}).partial().passthrough(), + volume: z.object({}).partial().passthrough(), + loadingMeters: z.number(), + dimensions: z + .object({ + width: z.object({}).partial().passthrough(), + height: z.object({}).partial().passthrough(), + length: z.object({}).partial().passthrough(), + }) + .partial() + .passthrough(), + references: z.array( + z + .object({ + number: z.string(), + type: z.enum([ + "customer-reference", + "customer-confirmation-number", + "local-tracking-number", + "ecommerce-number", + "housebill", + "masterbill", + "container-number", + "shipment-id", + "domestic-consignment-id", + "reference", + ]), + }) + .partial() + .passthrough() + ), + "dgf:routes": z.array(supermodelIoLogisticsTrackingDgfRoute), + }) + .partial() + .passthrough(); +const supermodelIoLogisticsTrackingShipment = z + .object({ + id: z.string(), + service: z.enum([ + "freight", + "express", + "post-de", + "parcel-de", + "parcel-nl", + "parcel-pl", + "parcel-uk", + "dsc", + "dgf", + "ecommerce", + "ecommerce-europe", + "svb", + ]), + origin: supermodelIoLogisticsSupportingPlace, + destination: supermodelIoLogisticsSupportingPlace, + status: supermodelIoLogisticsTrackingShipmentEvent, + estimatedTimeOfDelivery: supermodelIoLogisticsSupportingTimestamp, + estimatedDeliveryTimeFrame: z + .object({ + estimatedFrom: supermodelIoLogisticsSupportingTimestamp, + estimatedThrough: supermodelIoLogisticsSupportingTimestamp, + }) + .partial() + .passthrough(), + estimatedTimeOfDeliveryRemark: z.unknown(), + serviceUrl: z.unknown(), + rerouteUrl: z.unknown(), + details: supermodelIoLogisticsTrackingShipmentDetails, + events: z.array(supermodelIoLogisticsTrackingShipmentEvent), + }) + .partial() + .passthrough(); +const supermodelIoLogisticsTrackingShipments = z + .object({ + url: z.string(), + prevUrl: z.string(), + nextUrl: z.string(), + firstUrl: z.string(), + lastUrl: z.string(), + shipments: z.array(supermodelIoLogisticsTrackingShipment), + possibleAdditionalShipmentsUrl: z.array(z.string()), + }) + .partial() + .passthrough(); + +export const schemas = { + supermodelIoGs1PropertiesCountryCode, + supermodelIoLogisticsSupportingPlace, + supermodelIoSchemaorgDataType, + supermodelIoSchemaorgDateTime, + supermodelIoSchemaorgDate, + supermodelIoLogisticsSupportingTimestamp, + supermodelIoLogisticsTrackingShipmentEvent, + supermodelIoLogisticsSupportingOrganization, + supermodelIoLogisticsSupportingProduct, + supermodelIoLogisticsSupportingProvider, + supermodelIoLogisticsSupportingPerson, + supermodelIoLogisticsTrackingProofOfDelivery, + supermodelIoLogisticsTrackingDgfLocation, + supermodelIoLogisticsTrackingDgfAirport, + supermodelIoLogisticsTrackingDgfRoute, + supermodelIoLogisticsTrackingShipmentDetails, + supermodelIoLogisticsTrackingShipment, + supermodelIoLogisticsTrackingShipments, +}; diff --git a/packages/core/src/carriers/de.dhl/index.ts b/packages/core/src/carriers/de.dhl/index.ts new file mode 100644 index 0000000..d71037a --- /dev/null +++ b/packages/core/src/carriers/de.dhl/index.ts @@ -0,0 +1,397 @@ +import { type Logger } from "winston"; +import { + Carrier, + type CarrierTrackInput, + type TrackInfo, + type TrackEvent, + type TrackEventStatus, + TrackEventStatusCode, + type Location, + type ContactInfo, + type CarrierInitInput, +} from "../../core"; +import { rootLogger } from "../../logger"; +import { InternalError, NotFoundError } from "../../core/errors"; +import { schemas as DHLAPISchemas } from "./DHLAPISchemas"; +import { type z } from "zod"; +import { DateTime } from "luxon"; +import { type CarrierUpstreamFetcher } from "../../carrier-upstream-fetcher/CarrierUpstreamFetcher"; + +const carrierLogger = rootLogger.child({ + carrierId: "de.dhl", +}); + +// TODO : zod로 처리 +interface DHLConfig { + endpoint: string | null; + apiKey: string; + transformTimestamp?: ( + input: TransformTimestampInput + ) => Promise | null; +} + +interface TransformTimestampInput { + event: z.infer< + typeof DHLAPISchemas.supermodelIoLogisticsTrackingShipmentEvent + >; +} + +class DHL extends Carrier { + readonly carrierId = "de.dhl"; + private config: DHLConfig | null = null; + + public async init( + input: CarrierInitInput & { config: DHLConfig } + ): Promise { + await super.init(input); + this.config = input.config; + } + + public async track(input: CarrierTrackInput): Promise { + if (this.config == null) { + throw new Error("DHL is not initialized"); + } + + return await new DHLTrackScraper( + this.config, + this.upstreamFetcher, + input.trackingNumber + ).track(); + } +} + +class DHLTrackScraper { + private readonly logger: Logger; + + constructor( + readonly config: DHLConfig, + readonly upstreamFetcher: CarrierUpstreamFetcher, + readonly trackingNumber: string + ) { + this.logger = carrierLogger.child({ trackingNumber }); + } + + private get endpoint(): string { + if (this.config.endpoint == null) { + return "https://api-eu.dhl.com"; + } + return this.config.endpoint; + } + + public async track(): Promise { + const queryString = new URLSearchParams({ + trackingNumber: this.trackingNumber, + language: "en", + offset: "0", + limit: "1", + }).toString(); + + // https://developer.dhl/api-reference/shipment-tracking + const traceResponse = await this.upstreamFetcher.fetch( + `${this.endpoint}/track/shipments?${queryString}`, + { + headers: [ + ["accept", "application/json"], + ["DHL-API-Key", this.config.apiKey], + ], + } + ); + const traceResponseJson: z.infer< + typeof DHLAPISchemas.supermodelIoLogisticsTrackingShipments + > = await traceResponse.json(); + this.logger.debug("traceResponseJson", { + traceResponseJson, + }); + + if (traceResponse.status === 404) { + throw new NotFoundError(); + } + + if (traceResponse.status !== 200) { + throw new InternalError(); + } + + const safeParseResult = + await DHLAPISchemas.supermodelIoLogisticsTrackingShipments + .strict() + .safeParseAsync(traceResponseJson); + + if (!safeParseResult.success) { + this.logger.warn("response body parse failed (strict)", { + error: safeParseResult.error, + traceResponseJson, + }); + } + + const shipment = traceResponseJson.shipments?.at(0); + if (shipment === null) { + throw new InternalError(); + } + + const eventPromises = + shipment?.events?.map( + async (event) => await this.transformEvent(event) + ) ?? []; + return { + events: [...(await Promise.all(eventPromises))].reverse(), + sender: this.transformContact( + shipment?.details?.sender ?? null, + shipment?.origin ?? null + ), + recipient: this.transformContact( + shipment?.details?.receiver ?? null, + shipment?.destination ?? null + ), + carrierSpecificData: new Map(), + }; + } + + private async transformEvent( + event: z.infer< + typeof DHLAPISchemas.supermodelIoLogisticsTrackingShipmentEvent + > + ): Promise { + const transformTimestamp = + this.config.transformTimestamp ?? this.defaultTransformTimestamp(); + + let status: TrackEventStatus; + if (event.status === undefined) { + status = this.transformDescriptionToStatus(event.description ?? null); + } else { + status = { + code: this.transformStatusCode(event.statusCode ?? null), + name: event.status ?? null, + carrierSpecificData: new Map(), + }; + } + + return { + status, + time: await transformTimestamp({ + event, + }), + location: this.transformLocation(event.location ?? null), + contact: null, + description: event.description ?? event.status ?? null, + carrierSpecificData: new Map(), + }; + } + + private defaultTransformTimestamp(): ( + input: TransformTimestampInput + ) => Promise { + return async (input: TransformTimestampInput) => { + const timestamp = (input.event.timestamp as string | null) ?? null; + if (timestamp === null) { + return null; + } + const result = DateTime.fromISO(timestamp, { setZone: true }); + if (!result.isValid) { + this.logger.warn("time parse error", { + inputTime: timestamp, + invalidReason: result.invalidReason, + }); + } + return result; + }; + } + + private transformDescriptionToStatus( + description: string | null + ): TrackEventStatus { + if (description === null) { + this.logger.warn("transformDescriptionToStatus Unknown (null)", { + description, + }); + return { + code: TrackEventStatusCode.Unknown, + name: description, + carrierSpecificData: new Map(), + }; + } + + if (description === "Shipment picked up") { + return { + code: TrackEventStatusCode.AtPickup, + name: "Shipment picked up", + carrierSpecificData: new Map(), + }; + } + + if (description.startsWith("Processed at ")) { + return { + code: TrackEventStatusCode.InTransit, + name: "Processed", + carrierSpecificData: new Map(), + }; + } + + if (description.startsWith("Shipment has departed from a DHL facility")) { + return { + code: TrackEventStatusCode.InTransit, + name: "Shipment has departed from a DHL facility", + carrierSpecificData: new Map(), + }; + } + + if (description.startsWith("Shipment is in transit to destination")) { + return { + code: TrackEventStatusCode.InTransit, + name: "Shipment is in transit to destination", + carrierSpecificData: new Map(), + }; + } + + if (description.startsWith("Arrived at DHL Sort Facility")) { + return { + code: TrackEventStatusCode.InTransit, + name: "Arrived at DHL Sort Facility", + carrierSpecificData: new Map(), + }; + } + + if (description === "Shipment is on hold") { + return { + code: TrackEventStatusCode.InTransit, + name: "Shipment is on hold", + carrierSpecificData: new Map(), + }; + } + + if (description.startsWith("Customs clearance status updated.")) { + return { + code: TrackEventStatusCode.InTransit, + name: "Customs clearance status updated", + carrierSpecificData: new Map(), + }; + } + + if (description === "Clearance Event") { + return { + code: TrackEventStatusCode.InTransit, + name: "Clearance Event", + carrierSpecificData: new Map(), + }; + } + + if (description.startsWith("Processed for clearance at ")) { + return { + code: TrackEventStatusCode.InTransit, + name: "Processed for clearance", + carrierSpecificData: new Map(), + }; + } + + if (description.startsWith("Clearance processing complete at ")) { + return { + code: TrackEventStatusCode.InTransit, + name: "Clearance processing complete", + carrierSpecificData: new Map(), + }; + } + + if (description.startsWith("Arrived at DHL Delivery Facility")) { + return { + code: TrackEventStatusCode.InTransit, + name: "Arrived at DHL Delivery Facility", + carrierSpecificData: new Map(), + }; + } + + if (description === "Shipment is out with courier for delivery") { + return { + code: TrackEventStatusCode.OutForDelivery, + name: "Out for delivery", + carrierSpecificData: new Map(), + }; + } + + if (description === "Delivered") { + return { + code: TrackEventStatusCode.Delivered, + name: "Delivered", + carrierSpecificData: new Map(), + }; + } + + this.logger.warn("transformDescriptionToStatus Unknown", { + description, + }); + return { + code: TrackEventStatusCode.Unknown, + name: description, + carrierSpecificData: new Map(), + }; + } + + private transformStatusCode(statusCode: string | null): TrackEventStatusCode { + switch (statusCode) { + case "pre-transit": + return TrackEventStatusCode.InformationReceived; + case "transit": + return TrackEventStatusCode.InTransit; + case "delivered": + return TrackEventStatusCode.Delivered; + case "failure": + return TrackEventStatusCode.Exception; + case "unknown": + return TrackEventStatusCode.Unknown; + } + + this.logger.warn("parseStatusCode Unknown", { + statusCode, + }); + return TrackEventStatusCode.Unknown; + } + + private transformLocation( + location: z.infer< + typeof DHLAPISchemas.supermodelIoLogisticsSupportingPlace + > | null + ): Location | null { + if (location === null) { + return null; + } + return { + countryCode: location.address?.countryCode ?? null, + postalCode: location.address?.postalCode ?? null, + name: location.address?.addressLocality ?? null, + carrierSpecificData: new Map(), + }; + } + + private transformContact( + contact: + | (z.infer< + typeof DHLAPISchemas.supermodelIoLogisticsSupportingOrganization + > & + z.infer) + | null, + location: z.infer< + typeof DHLAPISchemas.supermodelIoLogisticsSupportingPlace + > | null + ): ContactInfo | null { + if (contact === null) { + return null; + } + + let name = contact.name ?? ""; + + if (contact.organizationName !== undefined) { + if (name === "") { + name = contact.organizationName; + } else { + name = `${contact.organizationName} - ${name}`; + } + } + + return { + name, + location: this.transformLocation(location), + phoneNumber: null, + carrierSpecificData: new Map(), + }; + } +} + +export { DHL }; diff --git a/packages/core/src/carriers/jp.sagawa/index.ts b/packages/core/src/carriers/jp.sagawa/index.ts new file mode 100644 index 0000000..48e5433 --- /dev/null +++ b/packages/core/src/carriers/jp.sagawa/index.ts @@ -0,0 +1,181 @@ +import { type Logger } from "winston"; +import { + Carrier, + type CarrierTrackInput, + type TrackInfo, + type TrackEvent, + TrackEventStatusCode, +} from "../../core"; +import { rootLogger } from "../../logger"; +import { + BadRequestError, + InternalError, + NotFoundError, +} from "../../core/errors"; +import { DateTime } from "luxon"; +import { JSDOM } from "jsdom"; +import { type CarrierUpstreamFetcher } from "../../carrier-upstream-fetcher/CarrierUpstreamFetcher"; + +const carrierLogger = rootLogger.child({ + carrierId: "jp.sagawa", +}); + +class Sagawa extends Carrier { + readonly carrierId = "jp.sagawa"; + + public async track(input: CarrierTrackInput): Promise { + return await new SagawaTrackScraper( + this.upstreamFetcher, + input.trackingNumber + ).track(); + } +} + +class SagawaTrackScraper { + private readonly logger: Logger; + + constructor( + readonly upstreamFetcher: CarrierUpstreamFetcher, + readonly trackingNumber: string + ) { + this.logger = carrierLogger.child({ trackingNumber }); + } + + public async track(): Promise { + const queryString = new URLSearchParams({ + okurijoNo: this.trackingNumber, + }).toString(); + const response = await this.upstreamFetcher.fetch( + `https://k2k.sagawa-exp.co.jp/p/web/okurijosearch.do?${queryString}` + ); + const traceResponseHtmlText = await response.text(); + this.logger.debug("traceResponseHtmlText", { + html: traceResponseHtmlText, + }); + + const dom = new JSDOM(traceResponseHtmlText); + const { document } = dom.window; + + if (document.querySelector("span.state")?.textContent === "該当なし") { + const message = document + .querySelector("#list1 tr:nth-child(2)") + ?.textContent?.replace(/\s+/g, " ") + ?.trim(); + if (message === "お荷物データが登録されておりません。") { + throw new NotFoundError(message); + } + throw new BadRequestError(message); + } + + const dd = document.querySelector('dd[id="detail1"]'); + if (dd === null) { + throw new InternalError(); + } + + const tables = dd.querySelectorAll("table"); + const eventTrs = tables[1].querySelectorAll("tr:not(:first-child)"); + const startDate = this.parseStartDate( + tables[0] + .querySelectorAll("td")[1] + .textContent?.replace(/\s+/g, " ") + ?.trim() ?? null + ); + + const events: TrackEvent[] = []; + for (const tr of eventTrs) { + events.push(this.parseEvent(tr, startDate)); + } + + return { + events, + sender: null, + recipient: null, + carrierSpecificData: new Map(), + }; + } + + private parseStartDate(text: string | null): DateTime { + const match = text?.match(/(\d{4}年\d{2}月\d{2}日)/) ?? null; + if (match === null) { + return DateTime.now().setZone("Asia/Tokyo"); + } + const result = DateTime.fromFormat(match[1], "yyyy年MM月dd日", { + zone: "Asia/Tokyo", + }); + if (!result.isValid) { + this.logger.warn("time parse error", { + inputTime: match[1], + invalidReason: result.invalidReason, + }); + } + return result; + } + + private parseEvent(tr: Element, startDate: DateTime): TrackEvent { + const tds = tr.querySelectorAll("td"); + const status = tds[0].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + const time = tds[1].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + const location = tds[2].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + + return { + status: { + code: this.parseStatusCode(status), + name: status, + carrierSpecificData: new Map(), + }, + time: this.parseTime(time, startDate), + location: { + name: location, + countryCode: "JP", + postalCode: null, + carrierSpecificData: new Map(), + }, + contact: null, + description: `${location ?? ""} - ${status ?? ""}`, + carrierSpecificData: new Map(), + }; + } + + private parseStatusCode(text: string | null): TrackEventStatusCode { + switch (text) { + case "↓集荷": + return TrackEventStatusCode.AtPickup; + case "↓輸送中": + return TrackEventStatusCode.InTransit; + case "↓配達中": + return TrackEventStatusCode.OutForDelivery; + case "⇒配達完了": + return TrackEventStatusCode.Delivered; + } + + this.logger.warn("Unexpected status code", { + text, + }); + return TrackEventStatusCode.Unknown; + } + + private parseTime(time: string | null, startDate: DateTime): DateTime | null { + if (time === null) { + return null; + } + const result = DateTime.fromFormat(time, "MM/dd HH:mm", { + zone: "Asia/Tokyo", + }); + if (!result.isValid) { + this.logger.warn("time parse error", { + inputTime: time, + invalidReason: result.invalidReason, + }); + return result; + } + + const estimatedTime = result.set({ year: startDate.year }); + if (startDate <= estimatedTime) { + return estimatedTime; + } else { + return estimatedTime.plus({ years: 1 }); + } + } +} + +export { Sagawa }; diff --git a/packages/core/src/carriers/kr.chunilps/index.ts b/packages/core/src/carriers/kr.chunilps/index.ts new file mode 100644 index 0000000..a10e21d --- /dev/null +++ b/packages/core/src/carriers/kr.chunilps/index.ts @@ -0,0 +1,295 @@ +import { + Carrier, + type ContactInfo, + type TrackEvent, + TrackEventStatusCode, + type TrackInfo, + type TrackEventStatus, + type CarrierTrackInput, +} from "../../core"; +import { rootLogger } from "../../logger"; +import { JSDOM } from "jsdom"; +import { NotFoundError } from "../../core/errors"; +import { parsePhoneNumber, type PhoneNumber } from "libphonenumber-js"; +import { type Logger } from "winston"; +import { DateTime } from "luxon"; +import { type CarrierUpstreamFetcher } from "../../carrier-upstream-fetcher/CarrierUpstreamFetcher"; + +const carrierLogger = rootLogger.child({ + carrierId: "kr.chunilps", +}); + +class Chunilps extends Carrier { + readonly carrierId = "kr.chunilps"; + + public async track(input: CarrierTrackInput): Promise { + return await new ChunilpsTrackScraper( + this.upstreamFetcher, + input.trackingNumber + ).track(); + } +} + +class ChunilpsTrackScraper { + private readonly logger: Logger; + private readonly carrierSpecificDataPrefix = "kr.chunilps"; + + constructor( + readonly upstreamFetcher: CarrierUpstreamFetcher, + readonly trackingNumber: string + ) { + this.logger = carrierLogger.child({ trackingNumber }); + } + + public async track(): Promise { + const queryString = new URLSearchParams({ + transNo: this.trackingNumber, + }).toString(); + const traceResponse = await this.upstreamFetcher.fetch( + `http://www.chunil.co.kr/HTrace/HTrace.jsp?${queryString}` + ); + + const traceResponseHtmlText = await traceResponse.text(); + this.logger.debug("traceResponseHtmlText", { + html: traceResponseHtmlText, + }); + + const dom = new JSDOM(traceResponseHtmlText); + const { document } = dom.window; + const tables = document.querySelectorAll('table[cellspacing="1"]'); + + if (tables.length === 0) { + throw new NotFoundError( + "운송장이 등록되지 않았거나 업체에서 상품을 준비중이니 업체로 문의해주시기 바랍니다." + ); + } + + if (tables.length !== 7) { + this.logger.warn("Unexpected table count", { + count: tables.length, + }); + } + + const senderElements = tables[0].querySelectorAll("td:nth-child(2n)"); + const recipientElements = tables[1].querySelectorAll("td:nth-child(2n)"); + const itemElements = tables[2].querySelectorAll("td:nth-child(2n)"); + const eventElements = tables[4].querySelectorAll("tr:not(:first-child)"); + + const sender: ContactInfo = { + name: senderElements[0].textContent?.trim() ?? null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }; + + const recipient: ContactInfo = { + name: recipientElements[0].textContent?.trim() ?? null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }; + + const events = this.parseEvents(eventElements); + const carrierSpecificData = new Map(); + + this.parseBranchInfoToCarrierSpecificData( + "dispatch", + senderElements[1].textContent?.trim() ?? null + ).forEach((value, key) => { + carrierSpecificData.set(key, value); + }); + + this.parseBranchInfoToCarrierSpecificData( + "arrival", + recipientElements[1].textContent?.trim() ?? null + ).forEach((value, key) => { + carrierSpecificData.set(key, value); + }); + + carrierSpecificData.set( + `${this.carrierSpecificDataPrefix}/item.name`, + itemElements[0].textContent?.trim() + ); + carrierSpecificData.set( + `${this.carrierSpecificDataPrefix}/item.quantity`, + itemElements[1].textContent?.trim() + ); + carrierSpecificData.set( + `${this.carrierSpecificDataPrefix}/item.cost`, + itemElements[2].textContent?.replace(/\s+/g, " ")?.trim() + ); + + return { + sender, + recipient, + events, + carrierSpecificData, + }; + } + + private parseEvents(eventElements: NodeListOf): TrackEvent[] { + const events: TrackEvent[] = []; + + for (const eventElement of eventElements) { + const tds = eventElement.querySelectorAll("td"); + if (tds.length !== 4) { + this.logger.warn("Unexpected td count", { + count: tds.length, + }); + } + + const contact = this.parseContact(tds[1].textContent, tds[2].textContent); + events.push({ + status: this.parseStatus(tds[3].textContent), + time: this.parseTime(tds[0].textContent), + contact, + location: contact?.location ?? null, + description: null, + carrierSpecificData: new Map(), + }); + } + return events; + } + + private parseStatus(text: string | null): TrackEventStatus { + return { + code: this.parseStatusCode(text), + name: text, + carrierSpecificData: new Map(), + }; + } + + private parseStatusCode(text: string | null): TrackEventStatusCode { + switch (text) { + case null: + return TrackEventStatusCode.Unknown; + case "접수": + return TrackEventStatusCode.InformationReceived; + case "발송": + return TrackEventStatusCode.AtPickup; + case "간선상차": + case "간선하차": + case "발송터미널하차": + case "발송터미널출발": + case "도착터미널하차": + case "영업소도착": + return TrackEventStatusCode.InTransit; + case "배송완료": + return TrackEventStatusCode.Delivered; + } + + this.logger.warn("Unexpected status code", { + text, + }); + + return TrackEventStatusCode.Unknown; + } + + private parseTime(time: string | null): DateTime | null { + if (time === null) { + return null; + } + const result = DateTime.fromISO(time, { zone: "Asia/Seoul" }); + if (!result.isValid) { + this.logger.warn("time parse error", { + inputTime: time, + invalidReason: result.invalidReason, + }); + } + return result; + } + + private parseContact( + name: string | null, + phoneNumber: string | null + ): ContactInfo | null { + let phoneNumberObj = null; + if (phoneNumber != null) { + try { + phoneNumberObj = parsePhoneNumber(phoneNumber, "KR"); + } catch (e) { + this.logger.warn("Failed to parse phone number (parseContact)", { + text: phoneNumber, + error: e, + }); + } + } + + return { + name: name ?? null, + phoneNumber: phoneNumberObj, + location: { + name: name ?? null, + countryCode: "KR", + postalCode: null, + carrierSpecificData: new Map(), + }, + carrierSpecificData: new Map(), + }; + } + + private parseBranchInfoToCarrierSpecificData( + branchType: string, + text: string | null + ): Map { + const carrierSpecificData = new Map(); + + if (text == null) { + return carrierSpecificData; + } + + const branch = this.parseBranchInfo(text); + carrierSpecificData.set( + `${this.carrierSpecificDataPrefix}/branch.${branchType}.name`, + branch.name + ); + if (branch.phoneNumber?.number != null) { + carrierSpecificData.set( + `${this.carrierSpecificDataPrefix}/branch.${branchType}.phoneNumber`, + branch.phoneNumber.number + ); + } + + return carrierSpecificData; + } + + private parseBranchInfo(text: string): ChunilpsBranchInfo { + const regex = /^(\S+)\s\((\d{3}-\d{3,4}-\d{4})?\)$/; + const match = text.match(regex); + + if (match == null) { + this.logger.warn("Failed to parse branch info", { + text, + }); + return { + name: text, + phoneNumber: null, + }; + } + + let phoneNumber = null; + + if (match[2] !== undefined) { + try { + phoneNumber = parsePhoneNumber(match[2], "KR"); + } catch (e) { + this.logger.warn("Failed to parse phone number", { + text, + error: e, + }); + } + } + + return { + name: match[1], + phoneNumber, + }; + } +} + +interface ChunilpsBranchInfo { + name: string; + phoneNumber: PhoneNumber | null; +} + +export { Chunilps }; diff --git a/packages/core/src/carriers/kr.cjlogistics/CJLogisticsAPISchemas.ts b/packages/core/src/carriers/kr.cjlogistics/CJLogisticsAPISchemas.ts new file mode 100644 index 0000000..85715d8 --- /dev/null +++ b/packages/core/src/carriers/kr.cjlogistics/CJLogisticsAPISchemas.ts @@ -0,0 +1,90 @@ +import { z } from "zod"; + +const CJLogisticsTrackingDetailResponseParcelResultMapResultSchema = z.object({ + /** Tracking Number */ + invcNo: z.string(), + /** sender name */ + sendrNm: z.string(), + /** recipient name */ + rcvrNm: z.string(), + /** item name (carrierSpecificData) */ + itemNm: z.string(), + /** item quantity (carrierSpecificData) */ + qty: z.string(), + /** unknown : delivery tracker unknown field (carrierSpecificData) */ + rgmailNo: z.string(), + /** unknown : delivery tracker unknown field (carrierSpecificData) */ + oriTrspbillnum: z.string(), + /** unknown : delivery tracker unknown field (carrierSpecificData) */ + rtnTrspbillnum: z.string(), + /** unknown : delivery tracker unknown field (carrierSpecificData) */ + nsDlvNm: z.string(), +}); +type CJLogisticsTrackingDetailResponseParcelResultMapResult = z.infer< + typeof CJLogisticsTrackingDetailResponseParcelResultMapResultSchema +>; + +const CJLogisticsTrackingDetailResponseParcelDetailResultMapResultSchema = + z.object({ + /** + * status full message (carrierSpecificData) + * ex) 배송지역으로 상품이 이동중입니다. + */ + crgNm: z.string(), + /** status code */ + crgSt: z.string(), + /** + * time + * ex) 2023-01-01 00:00:00.0 + */ + dTime: z.string(), + /** location id (carrierSpecificData) */ + regBranId: z.string(), + /** location name */ + regBranNm: z.string(), + /** + * status text + * ex) 간선상차 + */ + scanNm: z.string(), + // /** + // * unknown : delivery tracker unknown field (carrierSpecificData) + // */ + // nsDlNm: z.string(), + /** + * unknown : delivery tracker unknown field (carrierSpecificData) + */ + empImgNm: z.string(), + }); +type CJLogisticsTrackingDetailResponseParcelDetailResultMapResult = z.infer< + typeof CJLogisticsTrackingDetailResponseParcelDetailResultMapResultSchema +>; + +const CJLogisticsTrackingDetailResponseSchema = z.object({ + parcelResultMap: z.object({ + resultList: z.array( + CJLogisticsTrackingDetailResponseParcelResultMapResultSchema + ), + /** Tracking Number */ + paramInvcNo: z.string(), + }), + parcelDetailResultMap: z.object({ + resultList: z.array( + CJLogisticsTrackingDetailResponseParcelDetailResultMapResultSchema + ), + /** Tracking Number */ + paramInvcNo: z.string(), + }), +}); +type CJLogisticsTrackingDetailResponse = z.infer< + typeof CJLogisticsTrackingDetailResponseSchema +>; + +export { + CJLogisticsTrackingDetailResponseParcelResultMapResultSchema, + type CJLogisticsTrackingDetailResponseParcelResultMapResult, + CJLogisticsTrackingDetailResponseParcelDetailResultMapResultSchema, + type CJLogisticsTrackingDetailResponseParcelDetailResultMapResult, + CJLogisticsTrackingDetailResponseSchema, + type CJLogisticsTrackingDetailResponse, +}; diff --git a/packages/core/src/carriers/kr.cjlogistics/index.ts b/packages/core/src/carriers/kr.cjlogistics/index.ts new file mode 100644 index 0000000..ce0254a --- /dev/null +++ b/packages/core/src/carriers/kr.cjlogistics/index.ts @@ -0,0 +1,279 @@ +import { Cookie } from "tough-cookie"; +import * as cheerio from "cheerio"; +import { DateTime } from "luxon"; +import { parsePhoneNumber } from "libphonenumber-js"; +import { + Carrier, + type ContactInfo, + type Location, + type TrackEvent, + TrackEventStatusCode, + type TrackInfo, + type TrackEventStatus, + type CarrierTrackInput, +} from "../../core"; +import { BadRequestError, NotFoundError } from "../../core/errors"; +import { rootLogger } from "../../logger"; +import { + type CJLogisticsTrackingDetailResponse, + type CJLogisticsTrackingDetailResponseParcelDetailResultMapResult, + CJLogisticsTrackingDetailResponseSchema, +} from "./CJLogisticsAPISchemas"; +import { type Logger } from "winston"; +import { type CarrierUpstreamFetcher } from "../../carrier-upstream-fetcher/CarrierUpstreamFetcher"; + +const carrierLogger = rootLogger.child({ + carrierId: "kr.cjlogistics", +}); + +class CJLogistics extends Carrier { + readonly carrierId = "kr.cjlogistics"; + + public async track(input: CarrierTrackInput): Promise { + return await new CJLogisticsTrackScraper( + this.upstreamFetcher, + input.trackingNumber + ).track(); + } +} + +class CJLogisticsTrackScraper { + private readonly logger: Logger; + private readonly carrierSpecificDataPrefix = "kr.cjlogistics"; + + constructor( + readonly upstreamFetcher: CarrierUpstreamFetcher, + readonly trackingNumber: string + ) { + this.logger = carrierLogger.child({ trackingNumber }); + } + + public async track(): Promise { + if (!/^(\d{10}(\d{2})?)?$/.test(this.trackingNumber)) { + throw new BadRequestError("운송장 번호는 10자리 혹은 12자리입니다."); + } + + const mainPageResponse = await this.upstreamFetcher.fetch( + "https://www.cjlogistics.com/ko/tool/parcel/tracking" + ); + const cookieHeaders = + mainPageResponse.headers + .get("set-cookie") + ?.split(",") + .map((cookieString) => cookieString.trim()) + .map((cookieString) => Cookie.parse(cookieString)) + .map((cookie) => cookie?.cookieString() ?? null) + .join("; ") ?? null; + + const $ = cheerio.load(await mainPageResponse.text()); + const csrf = $("input[name=_csrf]").val() as string; + + const queryString = new URLSearchParams({ + paramInvcNo: this.trackingNumber, + _csrf: csrf, + }).toString(); + + const headers: Array<[string, string]> = []; + if (cookieHeaders !== null) { + headers.push(["cookie", cookieHeaders]); + } + + const trackingDetailResponse = await this.upstreamFetcher.fetch( + `https://www.cjlogistics.com/ko/tool/parcel/tracking-detail?${queryString}`, + { + method: "POST", + headers, + } + ); + + const trackingDetailBody: CJLogisticsTrackingDetailResponse = + await trackingDetailResponse.json(); + this.logger.debug("trackingDetailResponse", { + trackingDetailBody, + }); + + // 타입이 잘못된 경우 로깅만 하고 실제 동작은 최대한 돌아가게 한다. + const safeParseResult = + await CJLogisticsTrackingDetailResponseSchema.strict().safeParseAsync( + trackingDetailBody + ); + if (!safeParseResult.success) { + this.logger.warn("response body parse failed (strict)", { + error: safeParseResult.error, + trackingDetailBody, + }); + } + + const parcelResult = + trackingDetailBody.parcelResultMap.resultList.at(0) ?? null; + const events: TrackEvent[] = + trackingDetailBody.parcelDetailResultMap.resultList.map( + (parcelDetailResult) => { + return this.parseTrackEvent(parcelDetailResult); + } + ); + + if (parcelResult === null && events.length === 0) { + throw new NotFoundError(); + } + + if (parcelResult === null) { + return { + events, + sender: null, + recipient: null, + carrierSpecificData: new Map([]), + }; + } + + const sender: ContactInfo = { + name: parcelResult.sendrNm, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }; + const recipient: ContactInfo = { + name: parcelResult.rcvrNm, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }; + + return { + events, + sender, + recipient, + carrierSpecificData: new Map([ + [`${this.carrierSpecificDataPrefix}/raw/itemNm`, parcelResult.itemNm], + [`${this.carrierSpecificDataPrefix}/raw/qty`, parcelResult.qty], + [ + `${this.carrierSpecificDataPrefix}/raw/rgmailNo`, + parcelResult.rgmailNo, + ], + [ + `${this.carrierSpecificDataPrefix}/raw/oriTrspbillnum`, + parcelResult.oriTrspbillnum, + ], + [ + `${this.carrierSpecificDataPrefix}/raw/rtnTrspbillnum`, + parcelResult.rtnTrspbillnum, + ], + [`${this.carrierSpecificDataPrefix}/raw/nsDlvNm`, parcelResult.nsDlvNm], + ]), + }; + } + + private parseTrackEvent( + parcelDetailResult: CJLogisticsTrackingDetailResponseParcelDetailResultMapResult + ): TrackEvent { + return { + status: this.parseStatus(parcelDetailResult), + time: this.parseTime(parcelDetailResult.dTime), + location: this.parseLocation(parcelDetailResult), + contact: this.parseContact(parcelDetailResult), + description: parcelDetailResult.crgNm, + carrierSpecificData: new Map([ + // ["kr.cjlogistics/raw/nsDlNm", parcelDetailResult.nsDlNm], + [ + `${this.carrierSpecificDataPrefix}/raw/empImgNm`, + parcelDetailResult.empImgNm, + ], + ]), + }; + } + + private parseLocation( + parcelDetailResult: CJLogisticsTrackingDetailResponseParcelDetailResultMapResult + ): Location { + return { + countryCode: "KR", + name: parcelDetailResult.regBranNm, + postalCode: null, + carrierSpecificData: new Map([ + [ + `${this.carrierSpecificDataPrefix}/raw/regBranId`, + parcelDetailResult.regBranId, + ], + ]), + }; + } + + private parseContact( + parcelDetailResult: CJLogisticsTrackingDetailResponseParcelDetailResultMapResult + ): ContactInfo | null { + const regex = /([가-힣]{2,4})\s(\d{3}-\d{3,4}-\d{4})/; + const match = parcelDetailResult.crgNm.match(regex); + + if (match == null) { + return null; + } + + let phoneNumber = null; + try { + phoneNumber = parsePhoneNumber(match[2], "KR"); + } catch (e) { + this.logger.warn("parsePhoneNumber failed", { + error: e, + text: match[2], + }); + } + + return { + name: match[1], + location: this.parseLocation(parcelDetailResult), + phoneNumber, + carrierSpecificData: new Map(), + }; + } + + private parseTime(time: string): DateTime | null { + const result = DateTime.fromFormat(time, "yyyy-MM-dd HH:mm:ss", { + zone: "Asia/Seoul", + }); + if (!result.isValid) { + this.logger.warn("time parse error", { + inputTime: time, + invalidReason: result.invalidReason, + }); + } + return result; + } + + private parseStatus( + parcelDetailResult: CJLogisticsTrackingDetailResponseParcelDetailResultMapResult + ): TrackEventStatus { + return { + code: this.parseStatusCode(parcelDetailResult.crgSt), + name: parcelDetailResult.scanNm, + carrierSpecificData: new Map([ + [ + `${this.carrierSpecificDataPrefix}/raw/crgSt`, + parcelDetailResult.crgSt, + ], + ]), + }; + } + + private parseStatusCode(crgSt: string): TrackEventStatusCode { + switch (crgSt) { + case "11": + return TrackEventStatusCode.AtPickup; + case "21": + case "41": + case "42": + case "44": + return TrackEventStatusCode.InTransit; + case "82": + return TrackEventStatusCode.OutForDelivery; + case "91": + return TrackEventStatusCode.Delivered; + } + + this.logger.warn("parseStatusCode Unknown", { + crgSt, + }); + return TrackEventStatusCode.Unknown; + } +} + +export { CJLogistics }; diff --git a/packages/core/src/carriers/kr.cvsnet/CVSnetAPISchemas.ts b/packages/core/src/carriers/kr.cvsnet/CVSnetAPISchemas.ts new file mode 100644 index 0000000..201db80 --- /dev/null +++ b/packages/core/src/carriers/kr.cvsnet/CVSnetAPISchemas.ts @@ -0,0 +1,63 @@ +import { z } from "zod"; + +const CVSnetTrackingInfoResponseContactSchema = z.object({ + baseAddress: z.string(), + detailAddress: z.string(), + name: z.string(), + tel: z.string(), +}); + +type CVSnetTrackingInfoResponseContact = z.infer< + typeof CVSnetTrackingInfoResponseContactSchema +>; + +const CVSnetTrackingInfoResponseTrackingDetailSchema = z.object({ + /** "2023-01-01T00:00:00" */ + transTime: z.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/), + /** 3 */ + level: z.number(), + /** "남양주저온센터" */ + transWhere: z.string(), + /** "C02" */ + transCode: z.string(), + /** "점포 → (집화)일배 입고" */ + transKind: z.string(), +}); + +type CVSnetTrackingInfoResponseTrackingDetail = z.infer< + typeof CVSnetTrackingInfoResponseTrackingDetailSchema +>; + +const CVSnetTrackingInfoResponseSchema = z.object({ + /** GS네트웍스 */ + carrierName: z.string(), + /** GS_NETWORKS */ + carrierType: z.string(), + /** 200 */ + code: z.number(), + /** 잡화/서적 */ + goodsName: z.string(), + /** 000000000000 */ + invoiceNo: z.string(), + latestTrackingDetail: CVSnetTrackingInfoResponseTrackingDetailSchema, + receiver: CVSnetTrackingInfoResponseContactSchema, + sender: CVSnetTrackingInfoResponseContactSchema, + /** 반값택배 */ + serviceName: z.string(), + /** SLOW */ + serviceType: z.string(), + trackingDetails: z.array(CVSnetTrackingInfoResponseTrackingDetailSchema), +}); + +type CVSnetTrackingInfoResponse = z.infer< + typeof CVSnetTrackingInfoResponseSchema +>; + +export { + CVSnetTrackingInfoResponseContactSchema, + type CVSnetTrackingInfoResponseContact, + CVSnetTrackingInfoResponseTrackingDetailSchema, + type CVSnetTrackingInfoResponseTrackingDetail, + CVSnetTrackingInfoResponseSchema, + type CVSnetTrackingInfoResponse, +}; diff --git a/packages/core/src/carriers/kr.cvsnet/index.ts b/packages/core/src/carriers/kr.cvsnet/index.ts new file mode 100644 index 0000000..02adce1 --- /dev/null +++ b/packages/core/src/carriers/kr.cvsnet/index.ts @@ -0,0 +1,236 @@ +import { + Carrier, + type ContactInfo, + type TrackEvent, + TrackEventStatusCode, + type TrackInfo, + type CarrierTrackInput, +} from "../../core"; +import { rootLogger } from "../../logger"; +import { InternalError, NotFoundError } from "../../core/errors"; +import { type Logger } from "winston"; +import { + type CVSnetTrackingInfoResponse, + type CVSnetTrackingInfoResponseContact, + CVSnetTrackingInfoResponseSchema, + type CVSnetTrackingInfoResponseTrackingDetail, +} from "./CVSnetAPISchemas"; +import { DateTime } from "luxon"; +import { type CarrierUpstreamFetcher } from "../../carrier-upstream-fetcher/CarrierUpstreamFetcher"; + +const carrierLogger = rootLogger.child({ + carrierId: "kr.cvsnet", +}); + +class CVSnet extends Carrier { + readonly carrierId = "kr.cvsnet"; + + public async track(input: CarrierTrackInput): Promise { + return await new CVSnetTrackScraper( + this.upstreamFetcher, + input.trackingNumber + ).track(); + } +} + +class CVSnetTrackScraper { + private readonly logger: Logger; + private readonly carrierSpecificDataPrefix = "kr.cvsnet"; + + constructor( + readonly upstreamFetcher: CarrierUpstreamFetcher, + readonly trackingNumber: string + ) { + this.logger = carrierLogger.child({ trackingNumber }); + } + + public async track(): Promise { + const queryString = new URLSearchParams({ + invoice_no: this.trackingNumber, + }).toString(); + const deliveryResponse = await this.upstreamFetcher.fetch( + `https://www.cvsnet.co.kr/invoice/tracking.do?${queryString}` + ); + + const deliveryResponseHtmlText = await deliveryResponse.text(); + this.logger.debug("deliveryResponseHtmlText", { + html: deliveryResponseHtmlText, + }); + + const trackingInfoRegex = /var\s+trackingInfo\s*=\s*({[\s\S]*?});\s*\n/; + const trackingInfoRegexResult = + deliveryResponseHtmlText.match(trackingInfoRegex); + + if (trackingInfoRegexResult == null) { + this.logger.error("trackingInfoPattern not found", { + html: deliveryResponseHtmlText, + }); + throw new InternalError(); + } + + let trackingInfo: CVSnetTrackingInfoResponse | null = null; + try { + trackingInfo = JSON.parse(trackingInfoRegexResult[1]); + } catch (e) { + this.logger.error("trackingInfo parsing failed", { + rawJson: trackingInfoRegexResult[1], + }); + throw new InternalError(); + } + this.logger.debug("trackingInfo", { + data: trackingInfo, + }); + + if ( + typeof trackingInfo?.code === "number" && + [100, 400, 404].includes(trackingInfo?.code) + ) { + throw new NotFoundError( + // @ts-expect-error + trackingInfo?.msg ?? "Not found Tracking Number" + ); + } + + const safeParseResult = + await CVSnetTrackingInfoResponseSchema.strict().safeParseAsync( + trackingInfo + ); + + if (!safeParseResult.success) { + this.logger.warn("trackingInfo parse failed (strict)", { + error: safeParseResult.error, + trackingInfo, + }); + } + + if (trackingInfo == null) { + throw new InternalError(); + } + + return { + sender: this.parseContactInfo(trackingInfo.sender), + recipient: this.parseContactInfo(trackingInfo.receiver), + events: trackingInfo.trackingDetails.map((trackingDetail) => + this.parseTrackEvent(trackingDetail) + ), + carrierSpecificData: new Map([ + [ + `${this.carrierSpecificDataPrefix}/raw/carrierName`, + trackingInfo.carrierName, + ], + [ + `${this.carrierSpecificDataPrefix}/raw/carrierType`, + trackingInfo.carrierType, + ], + [`${this.carrierSpecificDataPrefix}/raw/code`, trackingInfo.code], + [ + `${this.carrierSpecificDataPrefix}/raw/goodsName`, + trackingInfo.goodsName, + ], + [ + `${this.carrierSpecificDataPrefix}/raw/serviceName`, + trackingInfo.serviceName, + ], + [ + `${this.carrierSpecificDataPrefix}/raw/serviceType`, + trackingInfo.serviceType, + ], + ]), + }; + } + + private parseContactInfo( + contact: CVSnetTrackingInfoResponseContact + ): ContactInfo { + return { + name: contact.name, + location: { + countryCode: "KR", + postalCode: null, + name: contact.name, + carrierSpecificData: new Map([ + [ + `${this.carrierSpecificDataPrefix}/raw/baseAddress`, + contact.baseAddress, + ], + [ + `${this.carrierSpecificDataPrefix}/raw/detailAddress`, + contact.detailAddress, + ], + ]), + }, + phoneNumber: { + "@type": "@delivery-tracker/core/MaskedPhoneNumber", + maskedPhoneNumber: `+82${contact.tel + .replaceAll("*", "X") + .replace(/^0+/, "")}`, + }, + carrierSpecificData: new Map(), + }; + } + + private parseTrackEvent( + trackingDetail: CVSnetTrackingInfoResponseTrackingDetail + ): TrackEvent { + return { + status: { + code: this.parseStatusCode(trackingDetail.transCode), + name: trackingDetail.transKind, + carrierSpecificData: new Map([ + [ + `${this.carrierSpecificDataPrefix}/raw/transCode`, + trackingDetail.transCode, + ], + ]), + }, + time: this.parseTime(trackingDetail.transTime), + location: { + countryCode: "KR", + name: trackingDetail.transWhere, + postalCode: null, + carrierSpecificData: new Map(), + }, + contact: null, + description: `${trackingDetail.transKind} 하였습니다.`, + carrierSpecificData: new Map([ + [`${this.carrierSpecificDataPrefix}/raw/level`, trackingDetail.level], + ]), + }; + } + + private parseStatusCode(transCode: string): TrackEventStatusCode { + switch (transCode) { + case "C01": // 점포접수 + return TrackEventStatusCode.InformationReceived; + case "C015": // 배송기사 인수 + return TrackEventStatusCode.AtPickup; + case "C02": // 점포 → (집화)일배 입고 + case "C03": // (집화)일배 → 허브 출고 + case "C04": // (집화)일배 → 허브 입고 + case "C07": // 허브 → (도착)일배 출고 + case "C09": // (도착)일배 → 점포 출고 + return TrackEventStatusCode.InTransit; + case "C095": // 배송기사 인계 + return TrackEventStatusCode.OutForDelivery; + case "C10": // 점포도착 + return TrackEventStatusCode.AvailableForPickup; + case "C11": // 고객전달 + return TrackEventStatusCode.Delivered; + } + + return TrackEventStatusCode.Unknown; + } + + private parseTime(time: string): DateTime | null { + const result = DateTime.fromISO(time, { zone: "Asia/Seoul" }); + if (!result.isValid) { + this.logger.warn("time parse error", { + inputTime: time, + invalidReason: result.invalidReason, + }); + } + return result; + } +} + +export { CVSnet }; diff --git a/packages/core/src/carriers/kr.cway/CwayAPISchemas.ts b/packages/core/src/carriers/kr.cway/CwayAPISchemas.ts new file mode 100644 index 0000000..8e173c5 --- /dev/null +++ b/packages/core/src/carriers/kr.cway/CwayAPISchemas.ts @@ -0,0 +1,30 @@ +import { z } from "zod"; + +const CwayDetailResponseDataSchema = z.object({ + /** receiver name */ + receiver: z.string(), +}); + +const CwayDetailResponseSchema = z.object({ + code: z.number(), + data: CwayDetailResponseDataSchema.nullable(), +}); + +const CwayLogListResponseRowSchema = z.object({ + logTime: z.string(), + /** 현재 위치 */ + logDetail: z.string(), + /** 배송상태 */ + logStatus: z.string(), +}); + +const CwayLogListResponseSchema = z.object({ + rows: z.array(CwayLogListResponseRowSchema), +}); + +export { + CwayDetailResponseDataSchema, + CwayDetailResponseSchema, + CwayLogListResponseRowSchema, + CwayLogListResponseSchema, +}; diff --git a/packages/core/src/carriers/kr.cway/index.ts b/packages/core/src/carriers/kr.cway/index.ts new file mode 100644 index 0000000..93b023f --- /dev/null +++ b/packages/core/src/carriers/kr.cway/index.ts @@ -0,0 +1,186 @@ +import { type Logger } from "winston"; +import { + Carrier, + type CarrierTrackInput, + type TrackInfo, + type TrackEvent, + TrackEventStatusCode, +} from "../../core"; +import { rootLogger } from "../../logger"; +import { InternalError, NotFoundError } from "../../core/errors"; +import { type z } from "zod"; +import { DateTime } from "luxon"; +import { + CwayDetailResponseSchema, + type CwayLogListResponseRowSchema, + CwayLogListResponseSchema, +} from "./CwayAPISchemas"; +import { type CarrierUpstreamFetcher } from "../../carrier-upstream-fetcher/CarrierUpstreamFetcher"; + +const carrierLogger = rootLogger.child({ + carrierId: "kr.cway", +}); + +class Cway extends Carrier { + readonly carrierId = "kr.cway"; + + public async track(input: CarrierTrackInput): Promise { + return await new CwayTrackScraper( + this.upstreamFetcher, + input.trackingNumber + ).track(); + } +} + +class CwayTrackScraper { + private readonly logger: Logger; + + constructor( + readonly upstreamFetcher: CarrierUpstreamFetcher, + readonly trackingNumber: string + ) { + this.logger = carrierLogger.child({ trackingNumber }); + } + + public async track(): Promise { + const detailResponse = await fetch("http://cway.hagoto.com/where/details", { + method: "POST", + headers: [ + ["content-type", "application/x-www-form-urlencoded; charset=UTF-8"], + ], + body: new URLSearchParams({ + hblNo: this.trackingNumber, + }).toString(), + }); + + const detailResponseBody: z.infer = + await detailResponse.json(); + this.logger.debug("trackingResponse - detailResponseBody", { + detailResponseBody, + }); + await this.safeParseLog(CwayDetailResponseSchema, detailResponseBody); + + if (detailResponseBody.code == 500) { + throw new NotFoundError(); + } + + if (detailResponseBody.data === null) { + throw new InternalError(); + } + + const logListResponse = await fetch( + "http://cway.hagoto.com/where/hbl/logList", + { + method: "POST", + headers: [["content-type", "application/x-www-form-urlencoded"]], + body: new URLSearchParams({ + hblNo: this.trackingNumber, + pageNum: "NaN", + isAsc: "asc", + }).toString(), + } + ); + + const logListResponseBody: z.infer = + await logListResponse.json(); + this.logger.debug("trackingResponse - logListResponseBody", { + logListResponseBody, + }); + + await this.safeParseLog(CwayLogListResponseSchema, logListResponseBody); + + const events = + logListResponseBody.rows.map((log) => this.transformEvent(log)) ?? []; + + return { + events, + sender: null, + recipient: { + name: detailResponseBody.data.receiver, + location: null, + phoneNumber: null, + carrierSpecificData: new Map([]), + }, + carrierSpecificData: new Map([]), + }; + } + + private async safeParseLog(schema: any, data: any): Promise { + const safeParseResult = await schema.strict().safeParseAsync(data); + if (!safeParseResult.success) { + this.logger.warn("response body parse failed (strict)", { + error: safeParseResult.error, + data, + }); + } + } + + private transformEvent( + log: z.infer + ): TrackEvent { + return { + status: { + code: this.transformStatusCode(log.logStatus), + name: log.logStatus, + carrierSpecificData: new Map(), + }, + time: this.parseTime(log.logTime), + location: { + name: log.logDetail.split(" ").at(0) ?? null, + countryCode: null, + postalCode: null, + carrierSpecificData: new Map(), + }, + contact: null, + description: log.logDetail ?? null, + carrierSpecificData: new Map(), + }; + } + + private transformStatusCode(logStatus: string): TrackEventStatusCode { + switch (logStatus) { + case "집하": + return TrackEventStatusCode.InTransit; + case "선적": + return TrackEventStatusCode.InTransit; + case "세관지정 장치장 반입": + return TrackEventStatusCode.InTransit; + case "수입통관 진행중": + return TrackEventStatusCode.InTransit; + case "세관지정 장치장 반출": + return TrackEventStatusCode.InTransit; + case "수입통관 완료": + return TrackEventStatusCode.InTransit; + case "집화처리": + return TrackEventStatusCode.InTransit; + case "간선하차": + return TrackEventStatusCode.InTransit; + case "간선상차": + return TrackEventStatusCode.InTransit; + case "배달출발": + return TrackEventStatusCode.OutForDelivery; + case "배달완료": + return TrackEventStatusCode.Delivered; + } + + this.logger.warn("parseStatusCode Unknown", { + logStatus, + }); + return TrackEventStatusCode.Unknown; + } + + private parseTime(time: string): DateTime | null { + const result = DateTime.fromFormat(time, "yyyy-MM-dd HH:mm:ss", { + zone: "Asia/Seoul", + }); + if (!result.isValid) { + this.logger.warn("time parse error", { + inputTime: time, + invalidReason: result.invalidReason, + }); + } + return result; + } +} + +export { Cway }; diff --git a/packages/core/src/carriers/kr.daesin/index.ts b/packages/core/src/carriers/kr.daesin/index.ts new file mode 100644 index 0000000..353f470 --- /dev/null +++ b/packages/core/src/carriers/kr.daesin/index.ts @@ -0,0 +1,279 @@ +import { type Logger } from "winston"; +import { + Carrier, + type CarrierTrackInput, + type TrackInfo, + type TrackEvent, + TrackEventStatusCode, + type MaskedPhoneNumber, + type TrackEventStatus, +} from "../../core"; +import { rootLogger } from "../../logger"; +import { + BadRequestError, + InternalError, + NotFoundError, +} from "../../core/errors"; +import { type z } from "zod"; +import { DateTime } from "luxon"; +import { JSDOM } from "jsdom"; +import { type PhoneNumber, parsePhoneNumber } from "libphonenumber-js"; +import { type CarrierUpstreamFetcher } from "../.."; +import * as IconvLite from "iconv-lite"; + +const carrierLogger = rootLogger.child({ + carrierId: "kr.daesin", +}); + +class Daesin extends Carrier { + readonly carrierId = "kr.daesin"; + + public async track(input: CarrierTrackInput): Promise { + return await new DaesinTrackScraper( + this.upstreamFetcher, + input.trackingNumber + ).track(); + } +} + +enum EventType { + Enter, + Leave, +} + +class DaesinTrackScraper { + private readonly logger: Logger; + + constructor( + readonly upstreamFetcher: CarrierUpstreamFetcher, + readonly trackingNumber: string + ) { + this.logger = carrierLogger.child({ trackingNumber }); + } + + public async track(): Promise { + const response = await this.upstreamFetcher.fetch( + "https://www.ds3211.co.kr/freight/internalFreightSearch.ht", + { + method: "POST", + headers: [["content-type", "application/x-www-form-urlencoded"]], + body: new URLSearchParams({ + billno: this.trackingNumber, + }).toString(), + } + ); + const traceResponseHtmlText = IconvLite.decode( + Buffer.from(await response.arrayBuffer()), + "euc-kr" + ); + this.logger.debug("traceResponseHtmlText", { + html: traceResponseHtmlText, + }); + + const dom = new JSDOM(traceResponseHtmlText); + const { document } = dom.window; + const printarea = document?.querySelector('div[id="printarea"]'); + + if (printarea === null) { + throw new InternalError(); + } + + const tables = printarea.querySelectorAll("table"); + + if (tables.length === 0) { + const message = + printarea + .querySelector("div.effect") + ?.textContent?.replace(/\s+/g, " ") + ?.trim() ?? undefined; + if (message?.endsWith("운송된 내역이 없습니다.") === true) { + throw new NotFoundError(message); + } + + throw new BadRequestError(message); + } + + const infoTds = tables[0].querySelectorAll("td"); + const eventTrs = tables[1].querySelectorAll("tr:not(:first-child)"); + + const events: TrackEvent[] = []; + for (const tr of eventTrs) { + events.push(...this.parseEvent(tr)); + } + + return { + sender: { + name: infoTds[0].textContent?.replace(/\s+/g, " ")?.trim() ?? null, + location: null, + phoneNumber: this.parseMaskedPhoneNumber( + infoTds[1].textContent?.replace(/\s+/g, " ")?.trim() ?? null + ), + carrierSpecificData: new Map(), + }, + recipient: { + name: infoTds[2].textContent?.replace(/\s+/g, " ")?.trim() ?? null, + location: null, + phoneNumber: this.parseMaskedPhoneNumber( + infoTds[3].textContent?.replace(/\s+/g, " ")?.trim() ?? null + ), + carrierSpecificData: new Map(), + }, + events, + carrierSpecificData: new Map(), + }; + } + + private parseMaskedPhoneNumber( + text: string | null + ): MaskedPhoneNumber | null { + if (text === null) { + return null; + } + return { + "@type": "@delivery-tracker/core/MaskedPhoneNumber", + maskedPhoneNumber: `+82${text.replaceAll("*", "X").replace(/^0+/, "")}`, + }; + } + + private parseEvent(tr: Element): TrackEvent[] { + const tds = tr.querySelectorAll("td"); + + // 중간 운송중인 경우 UI 상 운송 목적지 tr의 도착(접수)일시 / 출발(배달)일시 td 를 합쳐서 colspan="2" 로 표기하고 아무런 time이 표기되어 있지 않음 + if (tds[3].getAttribute("colspan") === "2") { + return []; + } + + const status = tds[0].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + + const location = { + name: tds[1].textContent?.replace(/\s+/g, " ")?.trim() ?? null, + countryCode: "KR", + postalCode: null, + carrierSpecificData: new Map(), + }; + const phoneNumber = this.parseLocationPhoneNumber( + tds[2].textContent?.replace(/\s+/g, " ")?.trim() ?? null + ); + + const enterTime = tds[3].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + const leaveTime = tds[4].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + const currentLocation = + tds[5].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + + const events = []; + for (const eventType of [EventType.Enter, EventType.Leave]) { + if ( + eventType === EventType.Leave && + (leaveTime === null || leaveTime === "") + ) { + continue; + } + events.push({ + status: this.parseStatus(status, eventType, currentLocation), + time: this.parseTime( + eventType === EventType.Enter ? enterTime : leaveTime + ), + location, + contact: { + name: location.name, + location, + phoneNumber, + carrierSpecificData: new Map(), + }, + description: null, + carrierSpecificData: new Map(), + }); + + if ( + eventType === EventType.Enter && + (currentLocation === "배달중" || currentLocation === "배송완료") + ) { + events.push({ + status: { + code: TrackEventStatusCode.OutForDelivery, + name: "배달중", + carrierSpecificData: new Map(), + }, + time: this.parseTime(enterTime), + location, + contact: { + name: location.name, + location, + phoneNumber, + carrierSpecificData: new Map(), + }, + description: null, + carrierSpecificData: new Map(), + }); + } + } + + return events; + } + + private parseLocationPhoneNumber(text: string | null): PhoneNumber | null { + const match = text?.match(/(\d{3}-\d{4}-\d{4})/g) ?? null; + if (match === null) { + return null; + } + try { + return parsePhoneNumber(match[0], "KR"); + } catch (e) { + this.logger.warn("parsePhoneNumber failed", { + error: e, + text: match[0], + }); + } + + return null; + } + + private parseStatus( + status: string | null, + eventType: EventType, + currentLocation: string | null + ): TrackEventStatus { + if (currentLocation === "배송완료" && eventType === EventType.Leave) { + return { + code: TrackEventStatusCode.Delivered, + name: "배송완료", + carrierSpecificData: new Map(), + }; + } + + if (status === "발송취급점" && eventType === EventType.Enter) { + return { + code: TrackEventStatusCode.InformationReceived, + name: "접수", + carrierSpecificData: new Map(), + }; + } + + return { + code: TrackEventStatusCode.InTransit, + name: `${status ?? ""} - ${ + eventType === EventType.Enter ? "도착" : "출발" + }`, + carrierSpecificData: new Map(), + }; + } + + private parseTime(time: string | null): DateTime | null { + if (time === null) { + return null; + } + + const result = DateTime.fromFormat(time, "yyyy-MM-dd HH:mm", { + zone: "Asia/Seoul", + }); + if (!result.isValid) { + this.logger.warn("time parse error", { + inputTime: time, + invalidReason: result.invalidReason, + }); + } + return result; + } +} + +export { Daesin }; diff --git a/packages/core/src/carriers/kr.epost/index.ts b/packages/core/src/carriers/kr.epost/index.ts new file mode 100644 index 0000000..9578a01 --- /dev/null +++ b/packages/core/src/carriers/kr.epost/index.ts @@ -0,0 +1,229 @@ +import { type Logger } from "winston"; +import { + Carrier, + type CarrierTrackInput, + type TrackInfo, + type TrackEvent, + TrackEventStatusCode, + type Location, + type ContactInfo, +} from "../../core"; +import { rootLogger } from "../../logger"; +import { NotFoundError } from "../../core/errors"; +import { DateTime } from "luxon"; +import { JSDOM } from "jsdom"; +import { type CarrierUpstreamFetcher } from "../../carrier-upstream-fetcher/CarrierUpstreamFetcher"; +import { map } from "cheerio/lib/api/traversing"; + +const carrierLogger = rootLogger.child({ + carrierId: "kr.epost", +}); + +class KoreaPost extends Carrier { + readonly carrierId = "kr.epost"; + + public async track(input: CarrierTrackInput): Promise { + return await new KoreaPostTrackScraper( + this.upstreamFetcher, + input.trackingNumber + ).track(); + } +} + +class KoreaPostTrackScraper { + private readonly logger: Logger; + + constructor( + readonly upstreamFetcher: CarrierUpstreamFetcher, + readonly trackingNumber: string + ) { + this.logger = carrierLogger.child({ trackingNumber }); + } + + public async track(): Promise { + const queryString = new URLSearchParams({ + sid1: this.trackingNumber, + }).toString(); + const response = await this.upstreamFetcher.fetch( + `https://service.epost.go.kr/trace.RetrieveDomRigiTraceList.comm?${queryString}` + ); + const traceResponseHtmlText = await response.text(); + this.logger.debug("traceResponseHtmlText", { + html: traceResponseHtmlText, + }); + + const dom = new JSDOM(traceResponseHtmlText); + const { document } = dom.window; + + const eventTrs = document.querySelectorAll("#processTable > tbody > tr"); + if (eventTrs.length < 1) { + throw new NotFoundError(); + } + + const events: TrackEvent[] = []; + for (const event of eventTrs) { + events.push(this.parseEvent(event)); + } + + const senderAndRecipient = this.parseSenderAndRecipient(document); + + return { + events, + ...senderAndRecipient, + carrierSpecificData: new Map(), + }; + } + + private parseEvent(tr: Element): TrackEvent { + const tds = tr.querySelectorAll("td"); + const date = tds[0].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + const time = tds[1].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + const location = tds[2].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + const status = tds[3].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + + return { + status: { + code: this.parseStatusCode(status), + name: status, + carrierSpecificData: new Map(), + }, + time: this.parseTime(date, time), + location: this.parseLocation(location), + contact: null, + description: `${status ?? ""} - ${location ?? ""}`, + carrierSpecificData: new Map(), + }; + } + + private parseStatusCode(status: string | null): TrackEventStatusCode { + if (status === null) { + this.logger.warn("status null"); + return TrackEventStatusCode.Unknown; + } + + switch (status) { + case "운송장출력": + return TrackEventStatusCode.InformationReceived; + case "접수": + return TrackEventStatusCode.InformationReceived; + case "접수 마감 후 접수(익일발송)": + return TrackEventStatusCode.InformationReceived; + case "발송": + return TrackEventStatusCode.InTransit; + case "도착": + return TrackEventStatusCode.InTransit; + } + + if (status.includes("배달준비")) { + return TrackEventStatusCode.OutForDelivery; + } + if (status.includes("배달완료")) { + return TrackEventStatusCode.Delivered; + } + if (status.includes("신청취소")) { + return TrackEventStatusCode.Exception; + } + if (status.includes("접수취소")) { + return TrackEventStatusCode.Exception; + } + if (status.includes("미배달")) { + return TrackEventStatusCode.AttemptFail; + } + if (status.includes("인수완료")) { + return TrackEventStatusCode.AtPickup; + } + if (status.includes("집하완료")) { + return TrackEventStatusCode.InTransit; + } + + this.logger.warn("Unexpected status code", { + status, + }); + return TrackEventStatusCode.Unknown; + } + + private parseTime(date: string | null, time: string | null): DateTime | null { + if (date === null) { + this.logger.warn("date or time null"); + return null; + } + if (time === null) { + this.logger.warn("date or time null"); + return null; + } + + const result = DateTime.fromFormat(`${date} ${time}`, "yyyy.MM.dd HH:mm", { + zone: "Asia/Seoul", + }); + + if (!result.isValid) { + this.logger.warn("time parse error", { + inputTime: time, + invalidReason: result.invalidReason, + }); + return result; + } + + return result; + } + + private parseLocation(location: string | null): Location | null { + if (location === null) { + this.logger.warn("location null"); + return null; + } + + return { + name: location, + countryCode: null, + postalCode: null, + carrierSpecificData: new Map(), + }; + } + + private parseSenderAndRecipient(document: Document): { + sender: ContactInfo | null; + recipient: ContactInfo | null; + } { + try { + const tds = + document + .querySelector("table.table_col > tbody") + ?.querySelectorAll("td") ?? []; + + return { + sender: this.parseContactInfo(tds[0] ?? null), + recipient: this.parseContactInfo(tds[1] ?? null), + }; + } catch (e) { + this.logger.warn("parseSenderAndRecipient error", { + error: e, + }); + return { + sender: null, + recipient: null, + }; + } + } + + private parseContactInfo(element: HTMLElement | null): ContactInfo | null { + if (element === null) { + return null; + } + const contactInfoHtml = element.innerHTML; + const contactInfoBrIndex = contactInfoHtml.indexOf("
"); + + let name = contactInfoHtml; + if (contactInfoBrIndex !== -1) { + name = contactInfoHtml.substring(0, contactInfoBrIndex); + } + return { + name: name.replace(/\s+/g, " ")?.trim(), + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }; + } +} + +export { KoreaPost }; diff --git a/packages/core/src/carriers/kr.hanjin/index.ts b/packages/core/src/carriers/kr.hanjin/index.ts new file mode 100644 index 0000000..b34c70e --- /dev/null +++ b/packages/core/src/carriers/kr.hanjin/index.ts @@ -0,0 +1,345 @@ +import { type Logger } from "winston"; +import { + Carrier, + type CarrierTrackInput, + type TrackInfo, + type TrackEvent, + TrackEventStatusCode, + type TrackEventStatus, +} from "../../core"; +import { rootLogger } from "../../logger"; +import { + BadRequestError, + InternalError, + NotFoundError, +} from "../../core/errors"; +import { DateTime } from "luxon"; +import { JSDOM } from "jsdom"; +import { type CarrierUpstreamFetcher } from "../../carrier-upstream-fetcher/CarrierUpstreamFetcher"; + +const carrierLogger = rootLogger.child({ + carrierId: "kr.hanjin", +}); + +class Hanjin extends Carrier { + readonly carrierId = "kr.hanjin"; + + public async track(input: CarrierTrackInput): Promise { + return await new HanjinTrackScraper( + this.upstreamFetcher, + input.trackingNumber + ).track(); + } +} + +class HanjinTrackScraper { + private readonly logger: Logger; + + constructor( + readonly upstreamFetcher: CarrierUpstreamFetcher, + readonly trackingNumber: string + ) { + this.logger = carrierLogger.child({ trackingNumber }); + } + + public async track(): Promise { + if (this.trackingNumber.match(/^[0-9]+$/) === null) { + throw new BadRequestError("잘못된 운송장번호 입니다."); + } + + if ( + this.trackingNumber.length !== 12 && + this.trackingNumber.length !== 14 + ) { + throw new BadRequestError("잘못된 운송장번호 입니다."); + } + + const response = await this.upstreamFetcher.fetch( + "https://www.hanjin.com/kor/CMS/DeliveryMgr/WaybillResult.do", + { + method: "POST", + headers: [["content-type", "application/x-www-form-urlencoded"]], + body: new URLSearchParams({ + wblnum: this.trackingNumber, + mCode: "MN038", + schLang: "KR", + }).toString(), + } + ); + if (response.status === 403) { + throw new InternalError("hanjin 403 error"); + } + const traceResponseHtmlText = await response.text(); + this.logger.debug("traceResponseHtmlText", { + html: traceResponseHtmlText, + }); + + // 상황에 따라 alert("message") 로 표시되는 경우가 있음 + if ( + traceResponseHtmlText.length < 2000 && + traceResponseHtmlText.includes("운송장이 등록되지 않") + ) { + throw new NotFoundError(); + } + + const dom = new JSDOM(traceResponseHtmlText); + const { document } = dom.window; + + const comm = document.querySelector(".comm-sec"); + if (comm != null) { + const message = comm.textContent?.trim() ?? ""; + if (message.includes("운송장이 등록되지 않")) { + throw new NotFoundError(message); + } + if (message.includes("잘못된 운송장")) { + throw new BadRequestError(message); + } + } + + const tables = document.querySelectorAll("table"); + const infoTds = tables[0].querySelectorAll("td"); + const eventTrs = tables[1].querySelectorAll("tr"); + + const events: TrackEvent[] = []; + for (const tr of eventTrs) { + const event = this.parseEvent(tr); + if (event !== null) { + events.push(event); + } + } + + return { + events, + sender: { + name: infoTds[1].textContent ?? null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }, + recipient: { + name: infoTds[2].textContent ?? null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }, + carrierSpecificData: new Map(), + }; + } + + private parseEvent(tr: Element): TrackEvent | null { + const tds = tr.querySelectorAll("td"); + // 간혹 형태가 섞여있음 + if (tds.length < 4) return null; + + const time = this.parseTime( + tds[0].textContent?.replace(/\s+/g, " ")?.trim() ?? null, + tds[1].textContent?.replace(/\s+/g, " ")?.trim() ?? null + ); + const location = tds[2].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + const description = + tds[3].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + + return { + status: this.parseStatus(description), + time, + location: { + name: location, + countryCode: "KR", + postalCode: null, + carrierSpecificData: new Map(), + }, + contact: null, + description, + carrierSpecificData: new Map(), + }; + } + + private parseStatus(description: string | null): TrackEventStatus { + if (description === null) { + return { + code: TrackEventStatusCode.Unknown, + name: null, + carrierSpecificData: new Map(), + }; + } + + if (description?.includes("통관목록이 접수")) { + return { + code: TrackEventStatusCode.InTransit, + name: "목록통관 접수", + carrierSpecificData: new Map(), + }; + } + + if (description?.includes("목록통관 심사가 완료")) { + return { + code: TrackEventStatusCode.InTransit, + name: "목록통관 심사 완료", + carrierSpecificData: new Map(), + }; + } + + if (description?.includes("하선신고가 수리")) { + return { + code: TrackEventStatusCode.InTransit, + name: "하선신고 수리", + carrierSpecificData: new Map(), + }; + } + + if (description?.includes("선편으로 출발 대기")) { + return { + code: TrackEventStatusCode.InTransit, + name: "출발 대기", + carrierSpecificData: new Map(), + }; + } + + if (description?.includes("수입신고가 접수")) { + return { + code: TrackEventStatusCode.InTransit, + name: "수입신고 접수", + carrierSpecificData: new Map(), + }; + } + + if (description?.includes("수입통관이 완료")) { + return { + code: TrackEventStatusCode.InTransit, + name: "수입통관 완료", + carrierSpecificData: new Map(), + }; + } + + if (description?.includes("수입신고가 수리")) { + return { + code: TrackEventStatusCode.InTransit, + name: "수입신고 수리", + carrierSpecificData: new Map(), + }; + } + + if (description?.includes("수입신고 진행")) { + return { + code: TrackEventStatusCode.InTransit, + name: "수입신고 진행", + carrierSpecificData: new Map(), + }; + } + + if (description?.includes("하선신고가 수리")) { + return { + code: TrackEventStatusCode.InTransit, + name: "하선신고 수리", + carrierSpecificData: new Map(), + }; + } + + if (description?.includes("접수")) { + return { + code: TrackEventStatusCode.InformationReceived, + name: "접수", + carrierSpecificData: new Map(), + }; + } + + if (description?.includes("로 이동중")) { + return { + code: TrackEventStatusCode.InTransit, + name: "터미널 상차", + carrierSpecificData: new Map(), + }; + } + + if (description?.includes("에 도착")) { + return { + code: TrackEventStatusCode.InTransit, + name: "터미널 하차", + carrierSpecificData: new Map(), + }; + } + + if (description?.includes("에 입고")) { + return { + code: TrackEventStatusCode.InTransit, + name: "터미널 하차", + carrierSpecificData: new Map(), + }; + } + + if (description?.includes("배송을 준비중")) { + return { + code: TrackEventStatusCode.InTransit, + name: "배송 준비", + carrierSpecificData: new Map(), + }; + } + + if (description?.includes("배송준비중")) { + return { + code: TrackEventStatusCode.InTransit, + name: "배송 준비", + carrierSpecificData: new Map(), + }; + } + + if (description?.includes("배송출발")) { + return { + code: TrackEventStatusCode.OutForDelivery, + name: "배송 출발", + carrierSpecificData: new Map(), + }; + } + + if (description?.includes("배송완료")) { + return { + code: TrackEventStatusCode.Delivered, + name: "배송 완료", + carrierSpecificData: new Map(), + }; + } + + if (description === "운송장 정보가 등록되었습니다.") { + return { + code: TrackEventStatusCode.InformationReceived, + name: "운송장 등록", + carrierSpecificData: new Map(), + }; + } + + this.logger.warn("Unexpected status code", { + description, + }); + + return { + code: TrackEventStatusCode.Unknown, + name: null, + carrierSpecificData: new Map(), + }; + } + + private parseTime(date: string | null, time: string | null): DateTime | null { + if (date === null) { + return null; + } + if (time === null) { + time = "00:00"; + } + + const result = DateTime.fromFormat(`${date} ${time}`, "yyyy-MM-dd HH:mm", { + zone: "Asia/Seoul", + }); + if (!result.isValid) { + this.logger.warn("time parse error", { + inputDate: date, + inputTime: time, + invalidReason: result.invalidReason, + }); + return result; + } + + return result; + } +} + +export { Hanjin }; diff --git a/packages/core/src/carriers/kr.honamlogis/HonamLogisAPISchemas.ts b/packages/core/src/carriers/kr.honamlogis/HonamLogisAPISchemas.ts new file mode 100644 index 0000000..1a2ab30 --- /dev/null +++ b/packages/core/src/carriers/kr.honamlogis/HonamLogisAPISchemas.ts @@ -0,0 +1,34 @@ +import { z } from "zod"; + +const HonamLogisTrackingInfoTrackTrackingDetailSchema = z.object({ + /* Date */ + SCAN_DM: z.string(), + /* status */ + SCANGB_NM: z.string(), + /* location name */ + SCAN_USER_NM: z.string(), +}); +type HonamLogisTrackingInfoTrackTrackingDetail = z.infer< + typeof HonamLogisTrackingInfoTrackTrackingDetailSchema +>; + +const HonamLogisTrackingInfoTrackSchema = z.object({ + TRACKING_DTL: z.array(HonamLogisTrackingInfoTrackTrackingDetailSchema), +}); + +const HonamLogisTrackingInfoResponseSchema = z.object({ + MSG_ID: z.string(), + ODS0_TOTAL: z.number(), + ODS0: z.array(HonamLogisTrackingInfoTrackSchema), +}); + +type HonamLogisTrackingInfoResponse = z.infer< + typeof HonamLogisTrackingInfoResponseSchema +>; + +export { + HonamLogisTrackingInfoTrackTrackingDetailSchema, + type HonamLogisTrackingInfoTrackTrackingDetail, + HonamLogisTrackingInfoResponseSchema, + type HonamLogisTrackingInfoResponse, +}; diff --git a/packages/core/src/carriers/kr.honamlogis/index.ts b/packages/core/src/carriers/kr.honamlogis/index.ts new file mode 100644 index 0000000..0d76ce5 --- /dev/null +++ b/packages/core/src/carriers/kr.honamlogis/index.ts @@ -0,0 +1,184 @@ +import { type Logger } from "winston"; +import { + Carrier, + type CarrierTrackInput, + type TrackInfo, + type TrackEvent, + TrackEventStatusCode, +} from "../../core"; +import { rootLogger } from "../../logger"; +import { NotFoundError } from "../../core/errors"; +import { DateTime } from "luxon"; +import { type CarrierUpstreamFetcher } from "../../carrier-upstream-fetcher/CarrierUpstreamFetcher"; +import { + HonamLogisTrackingInfoResponseSchema, + type HonamLogisTrackingInfoResponse, + type HonamLogisTrackingInfoTrackTrackingDetail, +} from "./HonamLogisAPISchemas"; + +const carrierLogger = rootLogger.child({ + carrierId: "kr.honamlogis", +}); + +class HonamLogis extends Carrier { + readonly carrierId = "kr.honamlogis"; + + public async track(input: CarrierTrackInput): Promise { + return await new HonamLogisTrackScraper( + this.upstreamFetcher, + input.trackingNumber + ).track(); + } +} + +class HonamLogisTrackScraper { + private readonly logger: Logger; + + constructor( + readonly upstreamFetcher: CarrierUpstreamFetcher, + readonly trackingNumber: string + ) { + this.logger = carrierLogger.child({ trackingNumber }); + } + + public async track(): Promise { + const response = await this.upstreamFetcher.fetch( + "http://inkoin.com/tracking_number.php", + { + method: "POST", + headers: { + "content-type": "application/x-www-form-urlencoded; charset=UTF-8", + }, + body: new URLSearchParams({ + SLIP_BARCD: this.trackingNumber, + }).toString(), + } + ); + + const traceResponseJson: HonamLogisTrackingInfoResponse = + await response.json(); + this.logger.debug("traceResponseJson", { + traceResponseJson, + }); + + const safeParseResult = + await HonamLogisTrackingInfoResponseSchema.strict().safeParseAsync( + traceResponseJson + ); + + if (!safeParseResult.success) { + this.logger.warn("traceResponseJson parse failed (strict)", { + error: safeParseResult.error, + traceResponseJson, + }); + } + + if (traceResponseJson.ODS0_TOTAL < 1) { + throw new NotFoundError(); + } + + const ods = traceResponseJson.ODS0[0]; + + const events: TrackEvent[] = []; + for (const event of ods.TRACKING_DTL) { + events.push(this.parseEvent(event)); + } + return { + events, + sender: { + name: null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }, + recipient: { + name: null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }, + carrierSpecificData: new Map(), + }; + } + + private parseEvent( + event: HonamLogisTrackingInfoTrackTrackingDetail + ): TrackEvent { + return { + status: { + code: this.parseStatusCode(event.SCANGB_NM), + name: event.SCANGB_NM, + carrierSpecificData: new Map(), + }, + time: this.parseTime(event.SCAN_DM), + location: { + name: event.SCAN_USER_NM, + countryCode: "KR", + postalCode: null, + carrierSpecificData: new Map(), + }, + contact: null, + description: this.parseDescription(event.SCANGB_NM), + carrierSpecificData: new Map(), + }; + } + + private parseStatusCode(status: string | null): TrackEventStatusCode { + switch (status) { + case "노선상차": + return TrackEventStatusCode.InTransit; + case "집하입고": + return TrackEventStatusCode.InTransit; + case "집하상차": + return TrackEventStatusCode.InTransit; + case "HUB T/M도착": + return TrackEventStatusCode.InTransit; + case "T/M출고": + return TrackEventStatusCode.InTransit; + case "터미널출고": + return TrackEventStatusCode.InTransit; + case "터미널입고": + return TrackEventStatusCode.InTransit; + case "노선하차": + return TrackEventStatusCode.InTransit; + case "영업소입고": + return TrackEventStatusCode.InTransit; + case "배송출발": + return TrackEventStatusCode.OutForDelivery; + case "배송완료": + return TrackEventStatusCode.Delivered; + } + + this.logger.warn("Unexpected status code", { + status, + }); + + return TrackEventStatusCode.Unknown; + } + + private parseDescription(status: string | null): string | null { + return status; + } + + private parseTime(time: string | null): DateTime | null { + if (time === null) { + return null; + } + + const result = DateTime.fromFormat(time, "yyyyMMddHHmmss", { + zone: "Asia/Seoul", + }); + + if (!result.isValid) { + this.logger.warn("time parse error", { + inputTime: time, + invalidReason: result.invalidReason, + }); + return result; + } + + return result; + } +} + +export { HonamLogis }; diff --git a/packages/core/src/carriers/kr.ilyanglogis/IlyangLogisAPISchemas.ts b/packages/core/src/carriers/kr.ilyanglogis/IlyangLogisAPISchemas.ts new file mode 100644 index 0000000..49527df --- /dev/null +++ b/packages/core/src/carriers/kr.ilyanglogis/IlyangLogisAPISchemas.ts @@ -0,0 +1,48 @@ +import { z } from "zod"; + +const IlyangLogisTrackingInfoResponseResultAPIBodyResultListTrackingSchema = + z.object({ + actDate: z.string(), + actTime: z.string(), + chkPointDesc: z.string(), + stationName: z.string(), + stationTel: z.string(), + }); + +type IlyangLogisTrackingInfoResponseResultAPIBodyResultListTracking = z.infer< + typeof IlyangLogisTrackingInfoResponseResultAPIBodyResultListTrackingSchema +>; + +const IlyangLogisTrackingInfoResponseResultAPIBodyResultListSchema = z.object({ + resultCode: z.string(), + lastTrackingDesc: z.string(), + resultDesc: z.string(), + tracking: z + .array(IlyangLogisTrackingInfoResponseResultAPIBodyResultListTrackingSchema) + .nullable(), +}); + +const IlyangLogisTrackingInfoResponseResultAPIBodySchema = z.object({ + resultList: z.array( + IlyangLogisTrackingInfoResponseResultAPIBodyResultListSchema + ), +}); + +const IlyangLogisTrackingInfoResponseResultAPISchema = z.object({ + body: IlyangLogisTrackingInfoResponseResultAPIBodySchema, +}); + +const IlyangLogisTrackingInfoResponseSchema = z.object({ + resultAPI: IlyangLogisTrackingInfoResponseResultAPISchema, +}); + +type IlyangLogisTrackingInfoResponse = z.infer< + typeof IlyangLogisTrackingInfoResponseSchema +>; + +export { + IlyangLogisTrackingInfoResponseSchema, + type IlyangLogisTrackingInfoResponse, + IlyangLogisTrackingInfoResponseResultAPIBodyResultListTrackingSchema, + type IlyangLogisTrackingInfoResponseResultAPIBodyResultListTracking, +}; diff --git a/packages/core/src/carriers/kr.ilyanglogis/index.ts b/packages/core/src/carriers/kr.ilyanglogis/index.ts new file mode 100644 index 0000000..05179c3 --- /dev/null +++ b/packages/core/src/carriers/kr.ilyanglogis/index.ts @@ -0,0 +1,189 @@ +import { type Logger } from "winston"; +import { + Carrier, + type CarrierTrackInput, + type TrackInfo, + type TrackEvent, + TrackEventStatusCode, +} from "../../core"; +import { rootLogger } from "../../logger"; +import { + BadRequestError, + InternalError, + NotFoundError, +} from "../../core/errors"; +import { DateTime } from "luxon"; +import { type CarrierUpstreamFetcher } from "../../carrier-upstream-fetcher/CarrierUpstreamFetcher"; +import { + type IlyangLogisTrackingInfoResponse, + IlyangLogisTrackingInfoResponseSchema, + type IlyangLogisTrackingInfoResponseResultAPIBodyResultListTracking, +} from "./IlyangLogisAPISchemas"; + +const carrierLogger = rootLogger.child({ + carrierId: "kr.ilyanglogis", +}); + +class IlyangLogis extends Carrier { + readonly carrierId = "kr.ilyanglogis"; + + public async track(input: CarrierTrackInput): Promise { + return await new IlyangLogisTrackScraper( + this.upstreamFetcher, + input.trackingNumber + ).track(); + } +} + +class IlyangLogisTrackScraper { + private readonly logger: Logger; + + constructor( + readonly upstreamFetcher: CarrierUpstreamFetcher, + readonly trackingNumber: string + ) { + this.logger = carrierLogger.child({ trackingNumber }); + } + + public async track(): Promise { + const response = await this.upstreamFetcher.fetch( + "https://www.ilyanglogis.co.kr/include/getAPIResult.asp", + { + method: "POST", + headers: { + "content-type": "application/x-www-form-urlencoded; charset=UTF-8", + }, + body: new URLSearchParams({ + req_type: "TRACKING", + tracking_type: "0", + blNum: this.trackingNumber, + }).toString(), + } + ); + + const traceResponseJson: IlyangLogisTrackingInfoResponse = + await response.json(); + this.logger.debug("traceResponseJson", { + traceResponseJson, + }); + + const safeParseResult = + await IlyangLogisTrackingInfoResponseSchema.strict().safeParseAsync( + traceResponseJson + ); + + if (!safeParseResult.success) { + this.logger.warn("traceResponseJson parse failed (strict)", { + error: safeParseResult.error, + traceResponseJson, + }); + } + + const result = traceResponseJson.resultAPI.body.resultList.at(0); + if (result === null || result === undefined) { + throw new InternalError(); + } + + if (result.tracking === null || result.tracking === undefined) { + const message = + typeof result.lastTrackingDesc === "string" && + result.lastTrackingDesc !== "" + ? result.lastTrackingDesc + : result.resultDesc ?? null; + + if (message.startsWith("미 접수된 물품")) { + throw new NotFoundError(message); + } + + throw new BadRequestError(message); + } + + const events: TrackEvent[] = []; + for (const event of result.tracking) { + events.push(this.parseEvent(event)); + } + return { + events, + sender: { + name: null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }, + recipient: { + name: null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }, + carrierSpecificData: new Map(), + }; + } + + private parseEvent( + event: IlyangLogisTrackingInfoResponseResultAPIBodyResultListTracking + ): TrackEvent { + return { + status: { + code: this.parseStatusCode(event.chkPointDesc), + name: event.chkPointDesc, + carrierSpecificData: new Map(), + }, + time: this.parseTime(event.actDate, event.actTime), + location: { + name: event.stationName, + countryCode: "KR", + postalCode: null, + carrierSpecificData: new Map(), + }, + contact: null, + description: this.parseDescription(event), + carrierSpecificData: new Map(), + }; + } + + private parseStatusCode(status: string | null): TrackEventStatusCode { + switch (status) { + case "발송사무소 인수": + return TrackEventStatusCode.AtPickup; + case "배송경유지 출고": + return TrackEventStatusCode.InTransit; + case "배송경유지 도착": + return TrackEventStatusCode.InTransit; + case "직원 배송중": + return TrackEventStatusCode.OutForDelivery; + case "배달완료": + return TrackEventStatusCode.Delivered; + } + + this.logger.warn("Unexpected status code", { + status, + }); + + return TrackEventStatusCode.Unknown; + } + + private parseDescription( + event: IlyangLogisTrackingInfoResponseResultAPIBodyResultListTracking + ): string | null { + return `${event.chkPointDesc} - ${event.stationName}`; + } + + private parseTime(date: string, time: string): DateTime | null { + const result = DateTime.fromFormat(`${date} ${time}`, "yyyyMMdd HHmm", { + zone: "Asia/Seoul", + }); + + if (!result.isValid) { + this.logger.warn("time parse error", { + inputTime: time, + invalidReason: result.invalidReason, + }); + return result; + } + + return result; + } +} + +export { IlyangLogis }; diff --git a/packages/core/src/carriers/kr.kdexp/KyungdongExpressAPISchemas.ts b/packages/core/src/carriers/kr.kdexp/KyungdongExpressAPISchemas.ts new file mode 100644 index 0000000..f0c7c0c --- /dev/null +++ b/packages/core/src/carriers/kr.kdexp/KyungdongExpressAPISchemas.ts @@ -0,0 +1,28 @@ +import { z } from "zod"; + +const KyungdongExpressTrackingResponseItemSchema = z.object({ + reg_date: z.string(), + stat: z.string(), + location: z.string(), + tel: z.string(), +}); + +type KyungdongExpressTrackingResponseItem = z.infer< + typeof KyungdongExpressTrackingResponseItemSchema +>; + +const KyungdongExpressTrackingResponseSchema = z.object({ + result: z.string().nullable(), + items: z.array(KyungdongExpressTrackingResponseItemSchema).nullable(), +}); + +type KyungdongExpressTrackingResponse = z.infer< + typeof KyungdongExpressTrackingResponseSchema +>; + +export { + KyungdongExpressTrackingResponseSchema, + type KyungdongExpressTrackingResponse, + KyungdongExpressTrackingResponseItemSchema, + type KyungdongExpressTrackingResponseItem, +}; diff --git a/packages/core/src/carriers/kr.kdexp/index.ts b/packages/core/src/carriers/kr.kdexp/index.ts new file mode 100644 index 0000000..ea92e41 --- /dev/null +++ b/packages/core/src/carriers/kr.kdexp/index.ts @@ -0,0 +1,169 @@ +import { type Logger } from "winston"; +import { + Carrier, + type CarrierTrackInput, + type TrackInfo, + type TrackEvent, + TrackEventStatusCode, +} from "../../core"; +import { rootLogger } from "../../logger"; +import { InternalError, NotFoundError } from "../../core/errors"; +import { DateTime } from "luxon"; +import { type CarrierUpstreamFetcher } from "../../carrier-upstream-fetcher/CarrierUpstreamFetcher"; +import { + type KyungdongExpressTrackingResponse, + KyungdongExpressTrackingResponseSchema, + type KyungdongExpressTrackingResponseItem, +} from "./KyungdongExpressAPISchemas"; + +const carrierLogger = rootLogger.child({ + carrierId: "kr.kdexp", +}); + +class KyungdongExpress extends Carrier { + readonly carrierId = "kr.kdexp"; + + public async track(input: CarrierTrackInput): Promise { + return await new KyungdongExpressTrackScraper( + this.upstreamFetcher, + input.trackingNumber + ).track(); + } +} + +class KyungdongExpressTrackScraper { + private readonly logger: Logger; + + constructor( + readonly upstreamFetcher: CarrierUpstreamFetcher, + readonly trackingNumber: string + ) { + this.logger = carrierLogger.child({ trackingNumber }); + } + + public async track(): Promise { + const queryString = new URLSearchParams({ + barcode: this.trackingNumber, + }).toString(); + + const response = await this.upstreamFetcher.fetch( + `https://kdexp.com/service/delivery/ajax_basic.do?${queryString}` + ); + + const traceResponseJson: KyungdongExpressTrackingResponse = + await response.json(); + this.logger.debug("traceResponseJson", { + traceResponseJson, + }); + + const safeParseResult = + await KyungdongExpressTrackingResponseSchema.strict().safeParseAsync( + traceResponseJson + ); + + if (!safeParseResult.success) { + this.logger.warn("traceResponseJson parse failed (strict)", { + error: safeParseResult.error, + traceResponseJson, + }); + } + + if (traceResponseJson.result !== "suc") { + throw new NotFoundError(); + } + + if ( + traceResponseJson.items === null || + traceResponseJson.items === undefined + ) { + throw new InternalError(); + } + + const events: TrackEvent[] = []; + for (const event of traceResponseJson.items) { + events.push(this.parseEvent(event)); + } + + return { + events, + sender: { + name: null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }, + recipient: { + name: null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }, + carrierSpecificData: new Map(), + }; + } + + private parseEvent(event: KyungdongExpressTrackingResponseItem): TrackEvent { + return { + status: { + code: this.parseStatusCode(event.stat), + name: event.stat, + carrierSpecificData: new Map(), + }, + time: this.parseTime(event.reg_date), + location: { + name: event.location, + countryCode: "KR", + postalCode: null, + carrierSpecificData: new Map(), + }, + contact: null, + description: this.parseDescription(event), + carrierSpecificData: new Map(), + }; + } + + private parseStatusCode(status: string | null): TrackEventStatusCode { + switch (status) { + case "접수완료": + return TrackEventStatusCode.InformationReceived; + case "영업소집하": + return TrackEventStatusCode.AtPickup; + case "터미널입고": + return TrackEventStatusCode.InTransit; + case "배달차량상차": + return TrackEventStatusCode.InTransit; + case "배송완료": + return TrackEventStatusCode.Delivered; + } + + this.logger.warn("Unexpected status code", { + status, + }); + + return TrackEventStatusCode.Unknown; + } + + private parseDescription( + event: KyungdongExpressTrackingResponseItem + ): string | null { + return `${event.stat} - ${event.location}`; + } + + private parseTime(time: string): DateTime | null { + const result = DateTime.fromFormat(time, "yyyy-MM-dd HH:mm:ss.u", { + zone: "Asia/Seoul", + }); + + if (!result.isValid) { + this.logger.warn("time parse error", { + inputTime: time, + invalidReason: result.invalidReason, + }); + return result; + } + + return result; + } +} + +export { KyungdongExpress }; diff --git a/packages/core/src/carriers/kr.kunyoung/index.ts b/packages/core/src/carriers/kr.kunyoung/index.ts new file mode 100644 index 0000000..0dc3968 --- /dev/null +++ b/packages/core/src/carriers/kr.kunyoung/index.ts @@ -0,0 +1,187 @@ +import { type Logger } from "winston"; +import { JSDOM } from "jsdom"; +import * as IconvLite from "iconv-lite"; +import { + Carrier, + type CarrierTrackInput, + type TrackInfo, + type TrackEvent, + TrackEventStatusCode, +} from "../../core"; +import { rootLogger } from "../../logger"; +import { InternalError, NotFoundError } from "../../core/errors"; +import { DateTime } from "luxon"; +import { type CarrierUpstreamFetcher } from "../../carrier-upstream-fetcher/CarrierUpstreamFetcher"; + +const carrierLogger = rootLogger.child({ + carrierId: "kr.kunyoung", +}); + +class Kunyoung extends Carrier { + readonly carrierId = "kr.kunyoung"; + + public async track(input: CarrierTrackInput): Promise { + return await new KunyoungTrackScraper( + this.upstreamFetcher, + input.trackingNumber + ).track(); + } +} + +class KunyoungTrackScraper { + private readonly logger: Logger; + + constructor( + readonly upstreamFetcher: CarrierUpstreamFetcher, + readonly trackingNumber: string + ) { + this.logger = carrierLogger.child({ trackingNumber }); + } + + public async track(): Promise { + const queryString = new URLSearchParams({ + mulno: this.trackingNumber, + }).toString(); + + const response = await this.upstreamFetcher.fetch( + `https://www.kunyoung.com/goods/goods_02.php?${queryString}` + ); + const traceResponseHtmlText = IconvLite.decode( + Buffer.from(await response.arrayBuffer()), + "euc-kr" + ); + this.logger.debug("traceResponseHtmlText", { + html: traceResponseHtmlText, + }); + + const dom = new JSDOM(traceResponseHtmlText); + const { document } = dom.window; + + const tables = document.querySelectorAll('table[width="717"]'); + if (tables.length !== 4) { + this.logger.warn("table count error"); + } + + const eventTrs = tables[3].querySelectorAll("tr:nth-child(2n+4)"); + + const events: TrackEvent[] = []; + for (const event of eventTrs) { + events.push(this.parseEvent(event)); + } + + if (this.isNotFound(events)) { + throw new NotFoundError(); + } + + return { + events, + sender: { + name: null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }, + recipient: { + name: null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }, + carrierSpecificData: new Map(), + }; + } + + private parseEvent(tr: Element): TrackEvent { + const tds = tr.querySelectorAll("td"); + const time = tds[0].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + const status = tds[2].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + return { + status: { + code: this.parseStatusCode(status), + name: status, + carrierSpecificData: new Map(), + }, + time: this.parseTime(time), + location: null, + contact: null, + description: status, + carrierSpecificData: new Map(), + }; + } + + private parseStatusCode(status: string | null): TrackEventStatusCode { + if (status === null) { + this.logger.warn("status code null"); + return TrackEventStatusCode.Unknown; + } + + if (status.endsWith("배송완료")) { + return TrackEventStatusCode.Delivered; + } + + if (status.endsWith("발송")) { + return TrackEventStatusCode.InTransit; + } + + if (status.endsWith("도착")) { + return TrackEventStatusCode.InTransit; + } + + this.logger.warn("Unexpected status code", { + status, + }); + + return TrackEventStatusCode.Unknown; + } + + private parseTime(time: string | null): DateTime | null { + if (time === null) { + this.logger.warn("time null"); + return null; + } + const result = DateTime.fromFormat(time, "yyyy-MM-dd HH:mm:ss", { + zone: "Asia/Seoul", + }); + + if (!result.isValid) { + this.logger.warn("time parse error", { + inputTime: time, + invalidReason: result.invalidReason, + }); + return result; + } + + return result; + } + + private isNotFound(events: TrackEvent[]): boolean { + if (events.length !== 4) return false; + if ( + events[0].time?.toISO() !== "2022-11-23T08:42:22.000+09:00" || + events[0].status?.name !== "도착" + ) + return false; + + if ( + events[1].time?.toISO() !== "2022-11-23T09:42:22.000+09:00" || + events[1].status?.name !== "영덕도착" + ) + return false; + + if ( + events[2].time?.toISO() !== "2022-12-01T06:45:10.000+09:00" || + events[2].status?.name !== "도착" + ) + return false; + + if ( + events[3].time?.toISO() !== "2022-12-01T09:08:43.000+09:00" || + events[3].status?.name !== "도착" + ) + return false; + + return true; + } +} + +export { Kunyoung }; diff --git a/packages/core/src/carriers/kr.logen/index.ts b/packages/core/src/carriers/kr.logen/index.ts new file mode 100644 index 0000000..a72aaeb --- /dev/null +++ b/packages/core/src/carriers/kr.logen/index.ts @@ -0,0 +1,200 @@ +import { type Logger } from "winston"; +import { JSDOM } from "jsdom"; +import { + Carrier, + type CarrierTrackInput, + type TrackInfo, + type TrackEvent, + TrackEventStatusCode, + type Location, +} from "../../core"; +import { rootLogger } from "../../logger"; +import { + BadRequestError, + InternalError, + NotFoundError, +} from "../../core/errors"; +import { DateTime } from "luxon"; +import { type CarrierUpstreamFetcher } from "../../carrier-upstream-fetcher/CarrierUpstreamFetcher"; + +const carrierLogger = rootLogger.child({ + carrierId: "kr.logen", +}); + +class Logen extends Carrier { + readonly carrierId = "kr.logen"; + + public async track(input: CarrierTrackInput): Promise { + return await new LogenTrackScraper( + this.upstreamFetcher, + input.trackingNumber + ).track(); + } +} + +class LogenTrackScraper { + private readonly logger: Logger; + + constructor( + readonly upstreamFetcher: CarrierUpstreamFetcher, + readonly trackingNumber: string + ) { + this.logger = carrierLogger.child({ trackingNumber }); + } + + public async track(): Promise { + if (!/^\d+$/.test(this.trackingNumber)) { + throw new BadRequestError( + "잘못된 운송장 번호 입니다. 운송장 번호는 숫자로만 이루어져 있습니다." + ); + } + + const response = await this.upstreamFetcher.fetch( + `https://www.ilogen.com/web/personal/trace/${encodeURIComponent( + this.trackingNumber + )}` + ); + const traceResponseHtmlText = await response.text(); + this.logger.debug("traceResponseHtmlText", { + html: traceResponseHtmlText, + }); + + if ( + traceResponseHtmlText.includes( + "alert('잘못된 접근입니다. 운송장번호를 확인해주세요.')" + ) + ) { + throw new BadRequestError( + "잘못된 접근입니다. 운송장번호를 확인해주세요." + ); + } + + const dom = new JSDOM(traceResponseHtmlText); + const { document } = dom.window; + + const empty = document.querySelector("tr.empty"); + if (empty !== null) { + const message = + empty.textContent?.replace(/\s+/g, " ")?.trim() ?? undefined; + throw new NotFoundError(message); + } + + const table = document.querySelector("table.data"); + if (table === null) { + throw new InternalError(); + } + + const eventTrs = table.querySelectorAll("tbody > tr"); + + const events: TrackEvent[] = []; + for (const event of eventTrs) { + events.push(this.parseEvent(event)); + } + + return { + events, + sender: { + name: null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }, + recipient: { + name: null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }, + carrierSpecificData: new Map(), + }; + } + + private parseEvent(tr: Element): TrackEvent { + const tds = tr.querySelectorAll("td"); + const time = tds[0].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + const location = tds[1].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + let status = tds[2].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + const description = + tds[3].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + + if (status === "배송완료 사진확인") { + status = "배송완료"; + } + + return { + status: { + code: this.parseStatusCode(status), + name: status, + carrierSpecificData: new Map(), + }, + time: this.parseTime(time), + location: this.parseLocation(location), + contact: null, + description, + carrierSpecificData: new Map(), + }; + } + + private parseStatusCode(status: string | null): TrackEventStatusCode { + switch (status) { + case "터미널입고": + return TrackEventStatusCode.InTransit; + case "터미널출고": + return TrackEventStatusCode.InTransit; + case "집하출고": + return TrackEventStatusCode.InTransit; + case "집하완료": + return TrackEventStatusCode.InTransit; + case "행낭적입": + return TrackEventStatusCode.InTransit; + case "배송입고": + return TrackEventStatusCode.InTransit; + case "배송출고": + return TrackEventStatusCode.OutForDelivery; + case "배송완료": + return TrackEventStatusCode.Delivered; + } + + this.logger.warn("Unexpected status code", { + status, + }); + + return TrackEventStatusCode.Unknown; + } + + private parseTime(time: string | null): DateTime | null { + if (time === null) { + this.logger.warn("time null"); + return null; + } + const result = DateTime.fromFormat(time, "yyyy.MM.dd HH:mm", { + zone: "Asia/Seoul", + }); + + if (!result.isValid) { + this.logger.warn("time parse error", { + inputTime: time, + invalidReason: result.invalidReason, + }); + return result; + } + + return result; + } + + private parseLocation(location: string | null): Location | null { + if (location === null) { + this.logger.warn("location null"); + return null; + } + + return { + name: location, + countryCode: "KR", + postalCode: null, + carrierSpecificData: new Map(), + }; + } +} + +export { Logen }; diff --git a/packages/core/src/carriers/kr.lotte/index.ts b/packages/core/src/carriers/kr.lotte/index.ts new file mode 100644 index 0000000..3b8fe42 --- /dev/null +++ b/packages/core/src/carriers/kr.lotte/index.ts @@ -0,0 +1,200 @@ +import { type Logger } from "winston"; +import { JSDOM } from "jsdom"; +import { + Carrier, + type CarrierTrackInput, + type TrackInfo, + type TrackEvent, + TrackEventStatusCode, + type Location, +} from "../../core"; +import { rootLogger } from "../../logger"; +import { + BadRequestError, + InternalError, + NotFoundError, +} from "../../core/errors"; +import { DateTime } from "luxon"; +import { type CarrierUpstreamFetcher } from "../../carrier-upstream-fetcher/CarrierUpstreamFetcher"; + +const carrierLogger = rootLogger.child({ + carrierId: "kr.lotte", +}); + +class LotteGlobalLogistics extends Carrier { + readonly carrierId = "kr.lotte"; + + public async track(input: CarrierTrackInput): Promise { + return await new LotteGlobalLogisticsTrackScraper( + this.upstreamFetcher, + input.trackingNumber + ).track(); + } +} + +class LotteGlobalLogisticsTrackScraper { + private readonly logger: Logger; + + constructor( + readonly upstreamFetcher: CarrierUpstreamFetcher, + readonly trackingNumber: string + ) { + this.logger = carrierLogger.child({ trackingNumber }); + } + + public async track(): Promise { + if (this.trackingNumber.length !== 12) { + throw new BadRequestError(); + } + if (!/^\d+$/.test(this.trackingNumber)) { + throw new BadRequestError(); + } + + const checksum = Number(this.trackingNumber.substring(11, 12)); + if (Number(this.trackingNumber.substring(0, 11)) % 7 !== checksum) { + throw new BadRequestError(); + } + + const response = await this.upstreamFetcher.fetch( + "https://www.lotteglogis.com/home/reservation/tracking/linkView", + { + method: "POST", + headers: { + "content-type": "application/x-www-form-urlencoded; charset=UTF-8", + }, + body: new URLSearchParams({ + InvNo: this.trackingNumber, + }).toString(), + } + ); + const traceResponseHtmlText = await response.text(); + this.logger.debug("traceResponseHtmlText", { + html: traceResponseHtmlText, + }); + + const dom = new JSDOM(traceResponseHtmlText); + const { document } = dom.window; + + const tables = document.querySelectorAll("table"); + if (tables.length !== 2) { + this.logger.warn("table count error"); + } + const eventTrs = tables[1].querySelectorAll("tbody > tr"); + + if ( + eventTrs.length === 1 && + eventTrs[0].querySelectorAll("td").length === 1 + ) { + const message = + eventTrs[0] + .querySelector("td") + ?.textContent?.replace(/\s+/g, " ") + ?.trim() ?? null; + throw new NotFoundError(); + } + + const events: TrackEvent[] = []; + for (const event of eventTrs) { + events.unshift(this.parseEvent(event)); + } + + return { + events, + sender: { + name: null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }, + recipient: { + name: null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }, + carrierSpecificData: new Map(), + }; + } + + private parseEvent(tr: Element): TrackEvent { + const tds = tr.querySelectorAll("td"); + const status = tds[0].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + const time = tds[1].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + const location = tds[2].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + const description = + tds[3].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + + return { + status: { + code: this.parseStatusCode(status), + name: status, + carrierSpecificData: new Map(), + }, + time: this.parseTime(time), + location: this.parseLocation(location), + contact: null, + description, + carrierSpecificData: new Map(), + }; + } + + private parseStatusCode(status: string | null): TrackEventStatusCode { + switch (status) { + case "인수/상품접수": + return TrackEventStatusCode.AtPickup; + case "상품 이동중": + return TrackEventStatusCode.InTransit; + case "배송 출발": + return TrackEventStatusCode.OutForDelivery; + case "배달 완료": + return TrackEventStatusCode.Delivered; + } + + this.logger.warn("Unexpected status code", { + status, + }); + + return TrackEventStatusCode.Unknown; + } + + private parseTime(time: string | null): DateTime | null { + if (time === null) { + this.logger.warn("time null"); + return null; + } + if (time.endsWith("--:--")) { + // TODO : 23:59 대신 이전 이벤트 기반으로 보정 필요 + time = `${time.substring(0, time.length - 5)}23:59`; + } + + const result = DateTime.fromFormat(time, "yyyy-MM-dd HH:mm", { + zone: "Asia/Seoul", + }); + + if (!result.isValid) { + this.logger.warn("time parse error", { + inputTime: time, + invalidReason: result.invalidReason, + }); + return result; + } + + return result; + } + + private parseLocation(location: string | null): Location | null { + if (location === null) { + this.logger.warn("location null"); + return null; + } + + return { + name: location, + countryCode: "KR", + postalCode: null, + carrierSpecificData: new Map(), + }; + } +} + +export { LotteGlobalLogistics }; diff --git a/packages/core/src/carriers/kr.slx/index.ts b/packages/core/src/carriers/kr.slx/index.ts new file mode 100644 index 0000000..dafea12 --- /dev/null +++ b/packages/core/src/carriers/kr.slx/index.ts @@ -0,0 +1,175 @@ +import { type Logger } from "winston"; +import { JSDOM } from "jsdom"; +import { + Carrier, + type CarrierTrackInput, + type TrackInfo, + type TrackEvent, + TrackEventStatusCode, + type Location, +} from "../../core"; +import { rootLogger } from "../../logger"; +import { + BadRequestError, + InternalError, + NotFoundError, +} from "../../core/errors"; +import { DateTime } from "luxon"; +import { type CarrierUpstreamFetcher } from "../../carrier-upstream-fetcher/CarrierUpstreamFetcher"; + +const carrierLogger = rootLogger.child({ + carrierId: "kr.slx", +}); + +class SLX extends Carrier { + readonly carrierId = "kr.slx"; + + public async track(input: CarrierTrackInput): Promise { + return await new SLXTrackScraper( + this.upstreamFetcher, + input.trackingNumber + ).track(); + } +} + +class SLXTrackScraper { + private readonly logger: Logger; + + constructor( + readonly upstreamFetcher: CarrierUpstreamFetcher, + readonly trackingNumber: string + ) { + this.logger = carrierLogger.child({ trackingNumber }); + } + + public async track(): Promise { + const queryString = new URLSearchParams({ + iv_no: this.trackingNumber, + }).toString(); + + const response = await this.upstreamFetcher.fetch( + `https://net.slx.co.kr/info/tracking.jsp?${queryString}` + ); + const traceResponseHtmlText = await response.text(); + this.logger.debug("traceResponseHtmlText", { + html: traceResponseHtmlText, + }); + + const dom = new JSDOM(traceResponseHtmlText); + const { document } = dom.window; + + const tables = document.querySelectorAll("table"); + if (tables.length !== 3) { + this.logger.warn("table count error"); + } + const eventTrs = tables[2].querySelectorAll("tbody > tr:not(:first-child)"); + if ( + eventTrs.length === 1 && + eventTrs[0].querySelector("td")?.textContent === "" + ) { + throw new NotFoundError(); + } + + const events: TrackEvent[] = []; + for (const event of eventTrs) { + events.push(this.parseEvent(event)); + } + return { + events, + sender: { + name: null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }, + recipient: { + name: null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }, + carrierSpecificData: new Map(), + }; + } + + private parseEvent(tr: Element): TrackEvent { + const tds = tr.querySelectorAll("td"); + const status = tds[0].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + const date = tds[1].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + const time = tds[2].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + const location = tds[3].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + + return { + status: { + code: this.parseStatusCode(status), + name: status, + carrierSpecificData: new Map(), + }, + time: this.parseTime(date, time), + location: this.parseLocation(location), + contact: null, + description: `${status ?? ""} - ${location ?? ""}`, + carrierSpecificData: new Map(), + }; + } + + private parseStatusCode(status: string | null): TrackEventStatusCode { + switch (status) { + case "상품집하": + return TrackEventStatusCode.AtPickup; + case "터미널 입고": + return TrackEventStatusCode.InTransit; + case "대리점 도착": + return TrackEventStatusCode.InTransit; + case "미배송": + return TrackEventStatusCode.AttemptFail; + case "배송출발": + return TrackEventStatusCode.OutForDelivery; + case "배송완료": + return TrackEventStatusCode.Delivered; + } + + this.logger.warn("Unexpected status code", { + status, + }); + + return TrackEventStatusCode.Unknown; + } + + private parseTime(date: string | null, time: string | null): DateTime | null { + if (date === null || time === null) { + this.logger.warn("date or time null"); + return null; + } + + const result = DateTime.fromFormat(`${date} ${time}`, "yyyy.MM.dd HH:mm", { + zone: "Asia/Seoul", + }); + + if (!result.isValid) { + this.logger.warn("time parse error", { + inputTime: time, + invalidReason: result.invalidReason, + }); + return result; + } + + return result; + } + + private parseLocation(location: string | null): Location | null { + if (location === null) { + this.logger.warn("location null"); + return null; + } + + return { + name: location, + countryCode: "KR", + postalCode: null, + carrierSpecificData: new Map(), + }; + } +} + +export { SLX }; diff --git a/packages/core/src/carriers/kr.todaypickup/index.ts b/packages/core/src/carriers/kr.todaypickup/index.ts new file mode 100644 index 0000000..5f065e1 --- /dev/null +++ b/packages/core/src/carriers/kr.todaypickup/index.ts @@ -0,0 +1,212 @@ +import { type Logger } from "winston"; +import { JSDOM } from "jsdom"; +import { + Carrier, + type CarrierTrackInput, + type TrackInfo, + type TrackEvent, + TrackEventStatusCode, + type Location, + type TrackEventStatus, +} from "../../core"; +import { rootLogger } from "../../logger"; +import { + BadRequestError, + InternalError, + NotFoundError, +} from "../../core/errors"; +import { DateTime } from "luxon"; +import { type CarrierUpstreamFetcher } from "../../carrier-upstream-fetcher/CarrierUpstreamFetcher"; + +const carrierLogger = rootLogger.child({ + carrierId: "kr.todaypickup", +}); + +class TodayPickup extends Carrier { + readonly carrierId = "kr.todaypickup"; + + public async track(input: CarrierTrackInput): Promise { + return await new TodayPickupTrackScraper( + this.upstreamFetcher, + input.trackingNumber + ).track(); + } +} + +class TodayPickupTrackScraper { + private readonly logger: Logger; + + constructor( + readonly upstreamFetcher: CarrierUpstreamFetcher, + readonly trackingNumber: string + ) { + this.logger = carrierLogger.child({ trackingNumber }); + } + + public async track(): Promise { + const response = await this.upstreamFetcher.fetch( + `https://mall.todaypickup.com/front/delivery/list/${encodeURIComponent( + this.trackingNumber + )}` + ); + const traceResponseHtmlText = await response.text(); + this.logger.debug("traceResponseHtmlText", { + html: traceResponseHtmlText, + }); + + const dom = new JSDOM(traceResponseHtmlText); + const { document } = dom.window; + + const tables = document.querySelectorAll("table"); + if (tables.length !== 3) { + this.logger.warn("table count error"); + } + const infoTds = tables[1].querySelectorAll("tbody > tr > td"); + if ((infoTds[0].textContent?.replace(/\s+/g, " ")?.trim() ?? "") === "") { + const message = infoTds[1].textContent?.replace(/\s+/g, " ")?.trim(); + + if (message?.includes("정보가 없") === true) { + throw new NotFoundError(); + } else { + throw new BadRequestError(); + } + } + const eventTrs = tables[2].querySelectorAll("tbody > tr"); + + const events: TrackEvent[] = []; + for (const event of eventTrs) { + events.push(this.parseEvent(event)); + } + + return { + events, + sender: { + name: null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }, + recipient: { + name: null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }, + carrierSpecificData: new Map(), + }; + } + + private parseEvent(tr: Element): TrackEvent { + const tds = tr.querySelectorAll("td"); + const time = tds[0].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + const location = tds[1].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + const description = + tds[2].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + + return { + status: this.parseStatus(description), + time: this.parseTime(time), + location: this.parseLocation(location), + contact: null, + description, + carrierSpecificData: new Map(), + }; + } + + private parseStatus(description: string | null): TrackEventStatus { + if (description === null) { + this.logger.error("description null"); + return { + code: TrackEventStatusCode.Unknown, + name: null, + carrierSpecificData: new Map(), + }; + } + + if (description.includes("접수")) { + return { + code: TrackEventStatusCode.InformationReceived, + name: "상품 접수", + carrierSpecificData: new Map(), + }; + } + + if (description.includes("수거")) { + return { + code: TrackEventStatusCode.AtPickup, + name: "상품 수거", + carrierSpecificData: new Map(), + }; + } + + if (description.includes("거점에 입고")) { + return { + code: TrackEventStatusCode.InTransit, + name: "거점 입고", + carrierSpecificData: new Map(), + }; + } + + if (description.includes("배송 중")) { + return { + code: TrackEventStatusCode.OutForDelivery, + name: "배송 중", + carrierSpecificData: new Map(), + }; + } + + if (description.includes("도착")) { + return { + code: TrackEventStatusCode.Delivered, + name: "배송완료", + carrierSpecificData: new Map(), + }; + } + + this.logger.warn("Unexpected status code", { + description, + }); + return { + code: TrackEventStatusCode.Unknown, + name: null, + carrierSpecificData: new Map(), + }; + } + + private parseTime(time: string | null): DateTime | null { + if (time === null) { + this.logger.warn("date or time null"); + return null; + } + + const result = DateTime.fromFormat(time, "yyyy.MM.dd HH:mm", { + zone: "Asia/Seoul", + }); + + if (!result.isValid) { + this.logger.warn("time parse error", { + inputTime: time, + invalidReason: result.invalidReason, + }); + return result; + } + + return result; + } + + private parseLocation(location: string | null): Location | null { + if (location === null) { + this.logger.warn("location null"); + return null; + } + + return { + name: location, + countryCode: "KR", + postalCode: null, + carrierSpecificData: new Map(), + }; + } +} + +export { TodayPickup }; diff --git a/packages/core/src/carriers/nl.tnt/TNTAPIAPISchemas.ts b/packages/core/src/carriers/nl.tnt/TNTAPIAPISchemas.ts new file mode 100644 index 0000000..1a9c097 --- /dev/null +++ b/packages/core/src/carriers/nl.tnt/TNTAPIAPISchemas.ts @@ -0,0 +1,58 @@ +import { z } from "zod"; + +const TNTTrackingResponseTrackerOutputConsignmentEventLocationSchema = z.object( + { + city: z.string(), + countryCode: z.string(), + } +); + +const TNTTrackingResponseTrackerOutputConsignmentEventSchema = z.object({ + date: z.string(), + statusDescription: z.string(), + location: TNTTrackingResponseTrackerOutputConsignmentEventLocationSchema, +}); + +const TNTTrackingResponseTrackerOutputConsignmentSchema = z.object({ + shipmentId: z.number(), + consignmentNumber: z.string(), + events: z.array(TNTTrackingResponseTrackerOutputConsignmentEventSchema), +}); + +const TNTTrackingResponseTrackerOutputSchema = z.object({ + consignment: z + .array(TNTTrackingResponseTrackerOutputConsignmentSchema) + .optional(), + notFound: z.any().optional(), +}); + +const TNTTrackingResponseSchema = z.object({ + "tracker.output": TNTTrackingResponseTrackerOutputSchema, +}); + +type TNTTrackingResponseTrackerOutputConsignmentEventLocation = z.infer< + typeof TNTTrackingResponseTrackerOutputConsignmentEventLocationSchema +>; +type TNTTrackingResponseTrackerOutputConsignmentEvent = z.infer< + typeof TNTTrackingResponseTrackerOutputConsignmentEventSchema +>; +type TNTTrackingResponseTrackerOutputConsignment = z.infer< + typeof TNTTrackingResponseTrackerOutputConsignmentSchema +>; +type TNTTrackingResponseTrackerOutput = z.infer< + typeof TNTTrackingResponseTrackerOutputSchema +>; +type TNTTrackingResponse = z.infer; + +export { + TNTTrackingResponseTrackerOutputConsignmentEventLocationSchema, + type TNTTrackingResponseTrackerOutputConsignmentEventLocation, + TNTTrackingResponseTrackerOutputConsignmentEventSchema, + type TNTTrackingResponseTrackerOutputConsignmentEvent, + TNTTrackingResponseTrackerOutputConsignmentSchema, + type TNTTrackingResponseTrackerOutputConsignment, + TNTTrackingResponseTrackerOutputSchema, + type TNTTrackingResponseTrackerOutput, + TNTTrackingResponseSchema, + type TNTTrackingResponse, +}; diff --git a/packages/core/src/carriers/nl.tnt/index.ts b/packages/core/src/carriers/nl.tnt/index.ts new file mode 100644 index 0000000..474e644 --- /dev/null +++ b/packages/core/src/carriers/nl.tnt/index.ts @@ -0,0 +1,170 @@ +import { type Logger } from "winston"; +import { + Carrier, + type CarrierTrackInput, + type TrackInfo, + type TrackEvent, + TrackEventStatusCode, +} from "../../core"; +import { rootLogger } from "../../logger"; +import { InternalError, NotFoundError } from "../../core/errors"; +import { DateTime } from "luxon"; +import { type CarrierUpstreamFetcher } from "../../carrier-upstream-fetcher/CarrierUpstreamFetcher"; +import { + type TNTTrackingResponse, + TNTTrackingResponseSchema, + type TNTTrackingResponseTrackerOutputConsignmentEvent, +} from "./TNTAPIAPISchemas"; + +const carrierLogger = rootLogger.child({ + carrierId: "nl.tnt", +}); + +class TNT extends Carrier { + readonly carrierId = "nl.tnt"; + + public async track(input: CarrierTrackInput): Promise { + return await new TNTTrackScraper( + this.upstreamFetcher, + input.trackingNumber + ).track(); + } +} + +class TNTTrackScraper { + private readonly logger: Logger; + + constructor( + readonly upstreamFetcher: CarrierUpstreamFetcher, + readonly trackingNumber: string + ) { + this.logger = carrierLogger.child({ trackingNumber }); + } + + public async track(): Promise { + const queryString = new URLSearchParams({ + con: this.trackingNumber, + locale: "en_GB", + searchType: "CON", + channel: "OPENTRACK", + }).toString(); + + const response = await this.upstreamFetcher.fetch( + `https://www.tnt.com/api/v3/shipment?${queryString}` + ); + + const traceResponseJson: TNTTrackingResponse = await response.json(); + this.logger.debug("traceResponseJson", { + traceResponseJson, + }); + + const safeParseResult = + await TNTTrackingResponseSchema.strict().safeParseAsync( + traceResponseJson + ); + + if (!safeParseResult.success) { + this.logger.warn("traceResponseJson parse failed (strict)", { + error: safeParseResult.error, + traceResponseJson, + }); + } + + if (traceResponseJson["tracker.output"]?.notFound !== undefined) { + throw new NotFoundError(); + } + + const defaultConsignment = + traceResponseJson["tracker.output"].consignment?.at(0); + if (defaultConsignment === undefined) { + throw new InternalError(); + } + + const events: TrackEvent[] = []; + for (const event of defaultConsignment.events) { + events.unshift(this.transformEvent(event)); + } + + return { + events, + sender: { + name: null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }, + recipient: { + name: null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }, + carrierSpecificData: new Map(), + }; + } + + private transformEvent( + event: TNTTrackingResponseTrackerOutputConsignmentEvent + ): TrackEvent { + return { + status: { + code: this.parseStatusCode(event.statusDescription), + name: event.statusDescription, + carrierSpecificData: new Map(), + }, + time: this.parseTime(event.date), + location: { + name: event.location.city, + countryCode: event.location.countryCode, + postalCode: null, + carrierSpecificData: new Map(), + }, + contact: null, + description: event.statusDescription, + carrierSpecificData: new Map(), + }; + } + + private parseStatusCode(status: string): TrackEventStatusCode { + switch (status) { + case "Shipment collected from collection address": + return TrackEventStatusCode.AtPickup; + case "Shipment arrived at connection point": + return TrackEventStatusCode.InTransit; + case "Shipment in transit": + return TrackEventStatusCode.InTransit; + case "Shipment now at the depot nearest to collection address": + return TrackEventStatusCode.InTransit; + case "Shipment now at depot nearest to delivery address": + return TrackEventStatusCode.InTransit; + case "Shipment arrived at TNT location": + return TrackEventStatusCode.InTransit; + case "Shipment left in agreed location as instructed": + return TrackEventStatusCode.Delivered; + case "Shipment delivered in good condition": + return TrackEventStatusCode.Delivered; + } + + this.logger.error("Unexpected status code", { + status, + }); + + return TrackEventStatusCode.Unknown; + } + + private parseTime(time: string): DateTime | null { + const result = DateTime.fromISO(time, { setZone: true }); + + if (!result.isValid) { + this.logger.warn("time parse error", { + inputTime: time, + invalidReason: result.invalidReason, + }); + return result; + } + + return result; + } +} + +export { TNT }; diff --git a/packages/core/src/carriers/un.upu.ems/index.ts b/packages/core/src/carriers/un.upu.ems/index.ts new file mode 100644 index 0000000..3b753be --- /dev/null +++ b/packages/core/src/carriers/un.upu.ems/index.ts @@ -0,0 +1,190 @@ +import { type Logger } from "winston"; +import { JSDOM } from "jsdom"; +import { + Carrier, + type CarrierTrackInput, + type TrackInfo, + type TrackEvent, + TrackEventStatusCode, + type Location, +} from "../../core"; +import { rootLogger } from "../../logger"; +import { DateTime } from "luxon"; +import { type CarrierUpstreamFetcher } from "../../carrier-upstream-fetcher/CarrierUpstreamFetcher"; +import { BadRequestError, NotFoundError } from "../../core/errors"; + +const carrierLogger = rootLogger.child({ + carrierId: "un.upu.ems", +}); + +class EMS extends Carrier { + readonly carrierId = "un.upu.ems"; + + public async track(input: CarrierTrackInput): Promise { + return await new EMSTrackScraper( + this.upstreamFetcher, + input.trackingNumber + ).track(); + } +} + +class EMSTrackScraper { + private readonly logger: Logger; + + constructor( + readonly upstreamFetcher: CarrierUpstreamFetcher, + readonly trackingNumber: string + ) { + this.logger = carrierLogger.child({ trackingNumber }); + } + + public async track(): Promise { + const queryString = new URLSearchParams({ + language: "EN", + itemId: this.trackingNumber, + }).toString(); + const response = await this.upstreamFetcher.fetch( + `https://items.ems.post/api/publicTracking/track?${queryString}` + ); + const traceResponseHtmlText = await response.text(); + this.logger.debug("traceResponseHtmlText", { + html: traceResponseHtmlText, + }); + + const dom = new JSDOM(traceResponseHtmlText); + const { document } = dom.window; + + const notFoundTd = document.querySelector("td.no-results-found"); + if (notFoundTd !== null) { + throw new NotFoundError( + notFoundTd.textContent?.replace(/\s+/g, " ")?.trim() + ); + } + + const errorUl = document.querySelector("ul.error"); + if (errorUl !== null) { + const errorMessage = errorUl.textContent?.replace(/\s+/g, " ")?.trim(); + throw new BadRequestError(errorMessage); + } + + const eventTrs = document.querySelectorAll("tbody > tr"); + + const events: TrackEvent[] = []; + for (const event of eventTrs) { + events.push(this.parseEvent(event)); + } + + return { + events, + sender: { + name: null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }, + recipient: { + name: null, + location: null, + phoneNumber: null, + carrierSpecificData: new Map(), + }, + carrierSpecificData: new Map(), + }; + } + + private parseEvent(tr: Element): TrackEvent { + const tds = tr.querySelectorAll("td"); + const time = tds[0].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + const status = tds[1].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + const location = tds[2].textContent?.replace(/\s+/g, " ")?.trim() ?? null; + + return { + status: { + code: this.parseStatusCode(status), + name: status, + carrierSpecificData: new Map(), + }, + time: this.parseTime(time), + location: this.parseLocation(location), + contact: null, + description: status, + carrierSpecificData: new Map(), + }; + } + + private parseStatusCode(status: string | null): TrackEventStatusCode { + if (status === null) { + this.logger.warn("status null"); + return TrackEventStatusCode.Unknown; + } + + switch (status) { + case "Posted": + return TrackEventStatusCode.AtPickup; + case "Arrived at export office": + return TrackEventStatusCode.InTransit; + case "Departed from export office": + return TrackEventStatusCode.InTransit; + case "Arrived at sorting center": + return TrackEventStatusCode.InTransit; + case "Arrived at destination import office": + return TrackEventStatusCode.InTransit; + case "Presented to import customs": + return TrackEventStatusCode.InTransit; + case "Held for customs inspection": + return TrackEventStatusCode.InTransit; + case "Released from import customs": + return TrackEventStatusCode.InTransit; + case "Departed from destination import office": + return TrackEventStatusCode.InTransit; + case "Arrived at post office": + return TrackEventStatusCode.InTransit; + case "Out for delivery": + return TrackEventStatusCode.OutForDelivery; + case "Delivered": + return TrackEventStatusCode.Delivered; + } + + this.logger.warn("Unexpected status code", { + status, + }); + return TrackEventStatusCode.Unknown; + } + + private parseTime(time: string | null): DateTime | null { + if (time === null) { + this.logger.warn("date or time null"); + return null; + } + + const result = DateTime.fromFormat(time, "MMM d, yyyy h:mm a", { + zone: "UTC", // FIX ME + }); + + if (!result.isValid) { + this.logger.warn("time parse error", { + inputTime: time, + invalidReason: result.invalidReason, + }); + return result; + } + + return result; + } + + private parseLocation(location: string | null): Location | null { + if (location === null) { + this.logger.warn("location null"); + return null; + } + + return { + name: location, + countryCode: null, + postalCode: null, + carrierSpecificData: new Map(), + }; + } +} + +export { EMS }; diff --git a/packages/core/src/carriers/us.fedex/index.ts b/packages/core/src/carriers/us.fedex/index.ts new file mode 100644 index 0000000..1d6dae8 --- /dev/null +++ b/packages/core/src/carriers/us.fedex/index.ts @@ -0,0 +1,385 @@ +import { type Logger } from "winston"; +import { + Carrier, + type CarrierTrackInput, + type TrackInfo, + type TrackEvent, + TrackEventStatusCode, + type Location, + type CarrierInitInput, +} from "../../core"; +import { rootLogger } from "../../logger"; +import { InternalError, NotFoundError } from "../../core/errors"; +import { type z } from "zod"; +import { DateTime } from "luxon"; +import { + CarrierUpstreamFetcher, + type CarrierUpstreamFetcherInitInput, +} from "../../carrier-upstream-fetcher/CarrierUpstreamFetcher"; + +const carrierLogger = rootLogger.child({ + carrierId: "us.fedex", +}); + +// TODO : zod로 처리 +interface FedexConfig { + endpoint: string | null; + clientId: string; + clientSecret: string; +} + +class FedexUpstreamFetcher extends CarrierUpstreamFetcher { + private readonly originUpstreamFetcher: CarrierUpstreamFetcher; + private readonly config: FedexConfig; + private accessToken: string | null = null; + + constructor( + input: CarrierUpstreamFetcherInitInput & { + originUpstreamFetcher: CarrierUpstreamFetcher; + config: FedexConfig; + } + ) { + super(input); + this.originUpstreamFetcher = input.originUpstreamFetcher; + this.config = input.config; + } + + get endpoint(): string { + if (this.config.endpoint == null) { + return "https://apis.fedex.com"; + } + return this.config.endpoint; + } + + async fetch( + input: RequestInfo | URL, + init?: RequestInit, + isRetry?: boolean + ): Promise { + if (this.accessToken === null) { + carrierLogger.info("fetch Fedex access token"); + const authResponse = await this.originUpstreamFetcher.fetch( + `${this.endpoint}/oauth/token`, + { + method: "POST", + headers: { + "content-type": "application/x-www-form-urlencoded", + }, + body: new URLSearchParams({ + grant_type: "client_credentials", + client_id: this.config.clientId, + client_secret: this.config.clientSecret, + }).toString(), + } + ); + if (authResponse.status !== 200) { + carrierLogger.error("auth response status error", { + status: authResponse.status, + }); + throw new InternalError(); + } + const authResponseBody = await authResponse.json(); + this.accessToken = authResponseBody.access_token as string; + carrierLogger.debug("accesstoken", { + accessToken: this.accessToken, + }); + } + + if (init === null || init === undefined) { + init = {}; + } + + if (init.headers === null || init.headers === undefined) { + init.headers = {}; + } + // @ts-expect-error + init.headers.authorization = `Bearer ${this.accessToken}`; + + const response = await this.originUpstreamFetcher.fetch(input, init); + if (response.status === 401) { + if (this.accessToken === null) { + return response; + } + this.accessToken = null; + + if (isRetry === true) { + return response; + } + return await this.fetch(input, init, true); + } + return response; + } +} + +class Fedex extends Carrier { + readonly carrierId = "us.fedex"; + private config: FedexConfig | null = null; + private fedexUpstreamFetcher: FedexUpstreamFetcher | null = null; + + public async init( + input: CarrierInitInput & { config: FedexConfig } + ): Promise { + await super.init(input); + this.config = input.config; + this.fedexUpstreamFetcher = new FedexUpstreamFetcher({ + carrier: this, + originUpstreamFetcher: this.upstreamFetcher, + config: this.config, + }); + } + + public async track(input: CarrierTrackInput): Promise { + if (this.config == null || this.fedexUpstreamFetcher == null) { + throw new Error("Fedex is not initialized"); + } + + return await new FedexTrackScraper( + this.config, + this.fedexUpstreamFetcher, + input.trackingNumber + ).track(); + } +} + +class FedexTrackScraper { + private readonly logger: Logger; + + constructor( + readonly config: FedexConfig, + readonly upstreamFetcher: FedexUpstreamFetcher, + readonly trackingNumber: string + ) { + this.logger = carrierLogger.child({ trackingNumber }); + } + + public async track(): Promise { + const requestBody = { + includeDetailedScans: true, + trackingInfo: [ + { + trackingNumberInfo: { + trackingNumber: this.trackingNumber, + }, + }, + ], + }; + + // https://developer.fedex.com/api/ko-kr/catalog/track/v1/docs.html + const traceResponse = await this.upstreamFetcher.fetch( + `${this.upstreamFetcher.endpoint}/track/v1/trackingnumbers`, + { + method: "POST", + headers: { + accept: "application/json", + "content-type": "application/json", + }, + body: JSON.stringify(requestBody), + } + ); + + const traceResponseJson = await traceResponse.json(); + this.logger.debug("traceResponseJson", { + traceResponseJson, + }); + + if (traceResponse.status !== 200) { + throw new InternalError(); + } + + const shipment = traceResponseJson.output?.completeTrackResults + ?.at(0) + ?.trackResults?.at(0); + + if (shipment === null) { + throw new InternalError(); + } + + if (shipment.error !== undefined && shipment.error !== null) { + if (shipment.error.code === "TRACKING.TRACKINGNUMBER.NOTFOUND") { + throw new NotFoundError(shipment.error.message ?? undefined); + } else { + throw new InternalError(shipment.error.message ?? undefined); + } + } + + const events: TrackEvent[] = []; + for (const event of shipment?.scanEvents ?? []) { + events.unshift(this.transformEvent(event)); + } + + return { + events, + sender: null, + recipient: null, + carrierSpecificData: new Map(), + }; + } + + private transformEvent(event: any): TrackEvent { + return { + status: { + code: this.transformStatusCode(event.derivedStatusCode ?? null), + name: event.derivedStatus ?? null, + carrierSpecificData: new Map(), + }, + time: this.parseTime(event.date ?? null), + location: this.parseLocation(event.scanLocation ?? null), + contact: null, + description: event.eventDescription ?? null, + carrierSpecificData: new Map(), + }; + } + + private transformStatusCode( + derivedStatusCode: string | null + ): TrackEventStatusCode { + if (derivedStatusCode === null) { + this.logger.warn("status null"); + return TrackEventStatusCode.Unknown; + } + + switch (derivedStatusCode) { + // Movement + case "AA": // At Airport + return TrackEventStatusCode.InTransit; + case "AC": // At Canada Post facility + return TrackEventStatusCode.InTransit; + case "AD": // At Delivery + return TrackEventStatusCode.InTransit; + case "AF": // At FedEx Facility + return TrackEventStatusCode.InTransit; + case "AO": // Shipment arriving On-time + return TrackEventStatusCode.InTransit; + case "AP": // At Pickup + return TrackEventStatusCode.AtPickup; + case "AR": // Arrived + return TrackEventStatusCode.InTransit; + case "AX": // At USPS facility + return TrackEventStatusCode.InTransit; + case "CA": // Shipment Cancelled + return TrackEventStatusCode.Exception; + case "CH": // Location Changed + return TrackEventStatusCode.InTransit; + case "DD": // Delivery Delay + return TrackEventStatusCode.InTransit; + case "DE": // Delivery Exception + return TrackEventStatusCode.Exception; + case "DL": // Delivered + return TrackEventStatusCode.Delivered; + case "DP": // Departed + return TrackEventStatusCode.InTransit; + case "DR": // Vehicle furnished but not used + return TrackEventStatusCode.InTransit; + case "DS": // Vehicle Dispatched + return TrackEventStatusCode.InTransit; + case "DY": // Delay + return TrackEventStatusCode.InTransit; + case "EA": // Enroute to Airport + return TrackEventStatusCode.InTransit; + case "ED": // Enroute to Delivery + return TrackEventStatusCode.InTransit; + case "EO": // Enroute to Origin Airport + return TrackEventStatusCode.InTransit; + case "EP": // Enroute to Pickup + return TrackEventStatusCode.InTransit; + case "FD": // At FedEx Destination + return TrackEventStatusCode.InTransit; + case "HL": // Hold at Location + return TrackEventStatusCode.InTransit; + case "IN": // Initiated + return TrackEventStatusCode.InformationReceived; + case "IT": // In Transit + return TrackEventStatusCode.InTransit; + case "IX": // In transit (see Details) + return TrackEventStatusCode.InTransit; + case "LO": // Left Origin + return TrackEventStatusCode.InTransit; + case "OC": // Order Created + return TrackEventStatusCode.InformationReceived; + case "OD": // Out for Delivery + return TrackEventStatusCode.OutForDelivery; + case "OF": // At FedEx origin facility + return TrackEventStatusCode.InTransit; + case "OX": // Shipment information sent to USPS + return TrackEventStatusCode.InTransit; + case "PD": // Pickup Delay + return TrackEventStatusCode.InTransit; + case "PF": // Plane in Flight + return TrackEventStatusCode.InTransit; + case "PL": // Plane Landed + return TrackEventStatusCode.InTransit; + case "PM": // In Progress + return TrackEventStatusCode.InTransit; + case "PU": // Picked Up + return TrackEventStatusCode.AtPickup; + case "PX": // Picked up (see Details) + return TrackEventStatusCode.AtPickup; + case "RR": // CDO requested + return TrackEventStatusCode.InTransit; + case "RM": // CDO Modified + return TrackEventStatusCode.InTransit; + case "RC": // CDO Cancelled + return TrackEventStatusCode.InTransit; + case "RS": // Return to Shipper + return TrackEventStatusCode.InTransit; + case "RP": // Return label link emailed to return sender + return TrackEventStatusCode.InTransit; + case "LP": // Return label link cancelled by shipment originator + return TrackEventStatusCode.InTransit; + case "RG": // Return label link expiring soon + return TrackEventStatusCode.InTransit; + case "RD": // Return label link expired + return TrackEventStatusCode.InTransit; + case "SE": // Shipment Exception + return TrackEventStatusCode.InTransit; + case "SF": // At Sort Facility + return TrackEventStatusCode.InTransit; + case "SP": // Split Status + return TrackEventStatusCode.InTransit; + case "TR": // Transfer + return TrackEventStatusCode.InTransit; + } + + this.logger.warn("Unexpected status code", { + derivedStatusCode, + }); + return TrackEventStatusCode.Unknown; + } + + private parseTime(time: string | null): DateTime | null { + if (time === null) { + this.logger.warn("date or time null"); + return null; + } + + const result = DateTime.fromISO(time, { + setZone: true, + }); + + if (!result.isValid) { + this.logger.warn("time parse error", { + inputTime: time, + invalidReason: result.invalidReason, + }); + return result; + } + + return result; + } + + private parseLocation(location: any | null): Location | null { + if (location === null) { + this.logger.warn("location null"); + return null; + } + + return { + name: location.city ?? null, + countryCode: location.countryCode ?? null, + postalCode: location.postalCode ?? null, + carrierSpecificData: new Map(), + }; + } +} + +export { Fedex }; diff --git a/packages/core/src/carriers/us.ups/index.ts b/packages/core/src/carriers/us.ups/index.ts new file mode 100644 index 0000000..fcb5469 --- /dev/null +++ b/packages/core/src/carriers/us.ups/index.ts @@ -0,0 +1,192 @@ +import { type Logger } from "winston"; +import { + Carrier, + type CarrierTrackInput, + type TrackInfo, + type TrackEvent, + TrackEventStatusCode, +} from "../../core"; +import { rootLogger } from "../../logger"; +import { BadRequestError, NotFoundError } from "../../core/errors"; +import { DateTime } from "luxon"; +import { type CarrierUpstreamFetcher } from "../../carrier-upstream-fetcher/CarrierUpstreamFetcher"; +import { Cookie } from "tough-cookie"; + +const carrierLogger = rootLogger.child({ + carrierId: "us.ups", +}); + +class UPS extends Carrier { + readonly carrierId = "us.ups"; + + public async track(input: CarrierTrackInput): Promise { + return await new UPSTrackScraper( + this.upstreamFetcher, + input.trackingNumber + ).track(); + } +} + +class UPSTrackScraper { + private readonly logger: Logger; + + constructor( + readonly upstreamFetcher: CarrierUpstreamFetcher, + readonly trackingNumber: string + ) { + this.logger = carrierLogger.child({ trackingNumber }); + } + + public async track(): Promise { + const trackPageResponse = await this.upstreamFetcher.fetch( + `https://www.ups.com/track?track=yes&trackNums=${encodeURIComponent( + this.trackingNumber + )}&loc=en_US` + ); + + const cookies = []; + for (const [name, value] of trackPageResponse.headers.entries()) { + if (name !== "set-cookie") continue; + const cookie = Cookie.parse(value); + if (cookie === null || cookie === undefined) continue; + if (cookie.domain !== "ups.com") continue; + cookies.push(cookie); + } + + const xsrfToken = + cookies.find((cookie) => cookie.key === "X-XSRF-TOKEN-ST")?.value ?? null; + + const headers: Array<[string, string]> = [ + ["Accept", "application/json"], + ["Content-Type", "application/json"], + ["Origin", "https://www.ups.com"], + [ + "User-Agent", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", + ], + ["Cookie", cookies.map((cookie) => cookie.cookieString()).join("; ")], + ]; + + if (xsrfToken !== null) { + headers.push(["X-Xsrf-Token", xsrfToken]); + } + const traceResponse = await this.upstreamFetcher.fetch( + "https://webapis.ups.com/track/api/Track/GetStatus?loc=en_US", + { + method: "POST", + headers, + body: JSON.stringify({ + Locale: "en_US", + TrackingNumber: [this.trackingNumber], + }), + } + ); + + const traceResponseJson = await traceResponse.json(); + this.logger.debug("traceResponseJson", { + traceResponseJson, + traceResponseStatus: traceResponse.status, + }); + + if (traceResponseJson?.statusCode === "402") { + throw new BadRequestError(); + } + + const trackDetails = traceResponseJson.trackDetails[0]; + if (trackDetails.errorCode === "504") { + throw new NotFoundError(); + } + + const events: TrackEvent[] = []; + for (const activity of trackDetails.shipmentProgressActivities ?? []) { + events.unshift(this.transformEvent(activity)); + } + + return { + events, + sender: null, + recipient: null, + carrierSpecificData: new Map(), + }; + } + + private transformEvent(activity: any): TrackEvent { + return { + status: { + code: this.transformStatusCode(activity.milestoneName?.nameKey ?? null), + name: activity.milestoneName?.name ?? null, + carrierSpecificData: new Map(), + }, + time: this.parseTime( + activity.gmtDate, + activity.gmtTime, + activity.gmtOffset + ), + location: null, + contact: null, + description: + activity?.activityScan ?? activity.milestoneName?.name ?? null, + carrierSpecificData: new Map(), + }; + } + + private transformStatusCode(nameKey: string | null): TrackEventStatusCode { + if (nameKey === null) { + this.logger.warn("nameKey null"); + return TrackEventStatusCode.Unknown; + } + + switch (nameKey) { + case "cms.stapp.orderReceived": + return TrackEventStatusCode.InformationReceived; + case "cms.stapp.outForDelivery": + return TrackEventStatusCode.OutForDelivery; + case "cms.stapp.inTransit": + return TrackEventStatusCode.InTransit; + case "cms.stapp.delivered": + return TrackEventStatusCode.Delivered; + } + + this.logger.warn("Unexpected nameKey", { + nameKey, + }); + return TrackEventStatusCode.Unknown; + } + + private parseTime( + gmtDate: string | null, + gmtTime: string | null, + gmtOffset: string | null + ): DateTime | null { + if (gmtDate === null || gmtTime === null || gmtOffset === null) { + this.logger.warn("gmtDate or gmtTime or gmtOffset null", { + gmtDate, + gmtTime, + gmtOffset, + }); + return null; + } + + const result = DateTime.fromFormat( + `${gmtDate} ${gmtTime}`, + "yyyyMMdd HH:mm:ss", + { + setZone: true, + } + ).setZone(`UTC${gmtOffset}`); + + if (!result.isValid) { + this.logger.warn("time parse error", { + gmtDate, + gmtTime, + gmtOffset, + invalidReason: result.invalidReason, + }); + return result; + } + + return result; + } +} + +export { UPS }; diff --git a/packages/core/src/carriers/us.usps/USPSAPISchemas.ts b/packages/core/src/carriers/us.usps/USPSAPISchemas.ts new file mode 100644 index 0000000..8c9921c --- /dev/null +++ b/packages/core/src/carriers/us.usps/USPSAPISchemas.ts @@ -0,0 +1,200 @@ +/** + * This file was automatically generated by the openapi-zod-client. + * API Spec: https://developer.dhl.com/api-reference/shipment-tracking + */ + +import { z } from "zod"; + +const TrackingSummary = z + .object({ + trackingNumber: z.string(), + expectedDeliveryDate: z.string(), + expectedDeliveryTime: z.string(), + guaranteedDeliveryDate: z.string(), + eventSummaries: z.array(z.string()).min(1).max(99), + }) + .partial(); +const AvailableNotificationOptions = z + .object({ + futureDelivery: z.boolean(), + alertDelivery: z.boolean(), + todayDelivery: z.boolean(), + UP: z.boolean(), + DND: z.boolean(), + }) + .partial(); +const EnabledNotificationRequests = z + .object({ + SMS: AvailableNotificationOptions, + EMail: AvailableNotificationOptions.and( + z + .object({ firstDisplayable: z.boolean(), otherActivity: z.boolean() }) + .partial() + .passthrough() + ), + }) + .partial(); +const TrackingDetails_extendRetentionExtraServiceCodeOptions = z + .object({ extendRetentionExtraServiceCodeOption: z.string() }) + .partial() + .passthrough(); +const TrackingEvent = z + .object({ + eventType: z.string(), + eventTimestamp: z.string().datetime({ offset: true }), + eventCountry: z.string(), + eventCity: z.string(), + eventState: z.string(), + eventZIP: z.string(), + firm: z.string().min(0).max(50), + name: z.string(), + authorizedAgent: z.boolean(), + eventCode: z.string(), + actionCode: z.string(), + reasonCode: z.string(), + }) + .partial(); +const TrackingDetails = z + .object({ + trackingNumber: z.string(), + additionalInfo: z.string(), + ADPScripting: z.string(), + archiveRestoreInfo: z.string(), + associatedLabel: z.string(), + carrierRelease: z.boolean(), + mailClass: z.enum([ + "BOUND_PRINTED_MATTER", + "LIBRARY_MAIL", + "USPS_RETAIL_GROUND", + "MEDIA_MAIL", + "CRITICAL_MAIL", + "PRIORITY_MAIL_INTERNATIONAL_PARCELS", + "DOMESTIC_MATTER_FOR_THE_BLIND", + "PRIORITY_MAIL_EXPRESS", + "FIRST-CLASS_MAIL", + "PRIORITY_MAIL_EXPRESS_INTERNATIONAL", + "FIRST-CLASS_PACKAGE_INTERNATIONAL_SERVICE", + "PARCEL_SELECT_LIGHTWEIGHT", + "PRIORITY_MAIL_GUARANTEED", + "GLOBAL_EXPRESS_GUARANTEED", + "PRIORITY_MAIL", + "PARCEL_SELECT", + "USPS_MARKETING_MAIL", + "USPS_MARKETING_MAIL", + "PRIORITY_MAIL_SAME_DAY", + ]), + destinationCity: z.string(), + destinationCountryCode: z.string(), + destinationState: z.object({}).partial().passthrough(), + destinationZIP: z.string(), + editedLabelId: z.string(), + emailEnabled: z.boolean(), + endOfDay: z.string(), + eSOFEligible: z.boolean(), + expectedDeliveryTimeStamp: z.string().datetime({ offset: true }), + expectedDeliveryType: z.string(), + guaranteedDeliveryTimeStamp: z.string().datetime({ offset: true }), + guaranteedDetails: z.string(), + itemShape: z.enum(["LETTER", "FLAT", "PARCEL", "UNKNOWN"]), + kahalaIndicator: z.boolean(), + mailType: z.enum([ + "INTERNATIONAL_INBOUND", + "INTERNATIONAL_OUTBOUND", + "DOMESTIC_MAIL", + "UNKNOWN", + ]), + approximateIntakeDate: z.string(), + uniqueTrackingId: z.string(), + onTime: z.boolean(), + originCity: z.string(), + originCountry: z.string().max(2), + originState: z.string().max(3), + originZIP: z.string().max(5), + proofOfDeliveryEnabled: z.boolean(), + predictedDeliveryTimeStamp: z.string().datetime({ offset: true }), + predictedDeliveryWindowStartTime: z.string(), + predictedDeliveryWindowEndTime: z.string(), + relatedReturnReceiptID: z.string(), + redeliveryEnabled: z.boolean(), + enabledNotificationRequests: EnabledNotificationRequests, + restoreEnabled: z.boolean(), + returnDateNotice: z.string(), + RRAMenabled: z.boolean(), + RREEnabled: z.boolean(), + services: z.array(z.string()), + serviceTypeCode: z.string(), + status: z.string(), + statusCategory: z.string(), + statusSummary: z.string(), + trackingProofOfDeliveryEnabled: z.boolean(), + valueofArticle: z.string(), + extendRetentionPurchasedCode: z.string(), + extendRetentionExtraServiceCodeOptions: z.array( + TrackingDetails_extendRetentionExtraServiceCodeOptions + ), + trackingEvents: z.array(TrackingEvent).max(99), + }) + .partial() + .passthrough(); +const inline_response_200 = z.union([TrackingSummary, TrackingDetails]); +const ErrorMessage_error_source = z + .object({ parameter: z.string(), example: z.string() }) + .partial() + .passthrough(); +const ErrorMessage_error_errors = z + .object({ + status: z.string(), + code: z.string(), + title: z.string(), + detail: z.string(), + source: ErrorMessage_error_source, + }) + .partial() + .passthrough(); +const ErrorMessage_error = z + .object({ + code: z.string(), + message: z.string(), + errors: z.array(ErrorMessage_error_errors), + }) + .partial() + .passthrough(); +const ErrorMessage = z + .object({ apiVersion: z.string(), error: ErrorMessage_error }) + .partial() + .passthrough(); +const MailingEventType = z.object({}).partial(); +const email = z.string(); +const cellNumber = z.string(); +const NotificationMethod = z.union([email, cellNumber]); +const TrackingNotificationRequest = z.object({ + uniqueTrackingId: z.number().int().optional(), + approximateIntakeDate: z.string().optional(), + notifyEventTypes: z.array(MailingEventType).min(1).max(7).optional(), + firstName: z.string().min(0).max(50).optional(), + lastName: z.string().min(0).max(50).optional(), + notifications: z.array(NotificationMethod).min(1).max(3), +}); +const TrackingNotificationAcknowledgement = z + .object({ resultText: z.string(), returnCode: z.string() }) + .partial(); + +export const schemas = { + TrackingSummary, + AvailableNotificationOptions, + EnabledNotificationRequests, + TrackingDetails_extendRetentionExtraServiceCodeOptions, + TrackingEvent, + TrackingDetails, + inline_response_200, + ErrorMessage_error_source, + ErrorMessage_error_errors, + ErrorMessage_error, + ErrorMessage, + MailingEventType, + email, + cellNumber, + NotificationMethod, + TrackingNotificationRequest, + TrackingNotificationAcknowledgement, +}; diff --git a/packages/core/src/carriers/us.usps/index.ts b/packages/core/src/carriers/us.usps/index.ts new file mode 100644 index 0000000..705d8fc --- /dev/null +++ b/packages/core/src/carriers/us.usps/index.ts @@ -0,0 +1,378 @@ +import { type Logger } from "winston"; +import { + Carrier, + type CarrierTrackInput, + type TrackInfo, + type TrackEvent, + TrackEventStatusCode, + type TrackEventStatus, + type CarrierInitInput, +} from "../../core"; +import { rootLogger } from "../../logger"; +import { + BadRequestError, + InternalError, + NotFoundError, +} from "../../core/errors"; +import { type z } from "zod"; +import { DateTime } from "luxon"; +import { + type CarrierUpstreamFetcherInitInput, + CarrierUpstreamFetcher, +} from "../../carrier-upstream-fetcher/CarrierUpstreamFetcher"; +import { schemas as USPSAPISchemas } from "./USPSAPISchemas"; + +const carrierLogger = rootLogger.child({ + carrierId: "us.usps", +}); + +// TODO : zod로 처리 +interface USPSConfig { + clientId: string; + clientSecret: string; +} + +class USPSUpstreamFetcher extends CarrierUpstreamFetcher { + private readonly originUpstreamFetcher: CarrierUpstreamFetcher; + private readonly config: USPSConfig; + private accessToken: string | null = null; + + constructor( + input: CarrierUpstreamFetcherInitInput & { + originUpstreamFetcher: CarrierUpstreamFetcher; + config: USPSConfig; + } + ) { + super(input); + this.originUpstreamFetcher = input.originUpstreamFetcher; + this.config = input.config; + } + + async fetch( + input: RequestInfo | URL, + init?: RequestInit, + isRetry?: boolean + ): Promise { + if (this.accessToken === null) { + carrierLogger.info("fetch USPS access token"); + const authResponse = await this.originUpstreamFetcher.fetch( + "https://api.usps.com/oauth2/v3/token", + { + method: "POST", + headers: { + "content-type": "application/x-www-form-urlencoded", + }, + body: new URLSearchParams({ + grant_type: "client_credentials", + client_id: this.config.clientId, + client_secret: this.config.clientSecret, + }).toString(), + } + ); + if (authResponse.status !== 200) { + carrierLogger.error("auth response status error", { + status: authResponse.status, + }); + throw new InternalError(); + } + const authResponseBody = await authResponse.json(); + this.accessToken = authResponseBody.access_token as string; + carrierLogger.debug("accesstoken", { + accessToken: this.accessToken, + }); + } + + if (init === null || init === undefined) { + init = {}; + } + + if (init.headers === null || init.headers === undefined) { + init.headers = {}; + } + // @ts-expect-error + init.headers.authorization = `Bearer ${this.accessToken}`; + + const response = await this.originUpstreamFetcher.fetch(input, init); + if (response.status === 401) { + if (this.accessToken === null) { + return response; + } + this.accessToken = null; + + if (isRetry === true) { + return response; + } + return await this.fetch(input, init, true); + } + return response; + } +} + +class USPS extends Carrier { + readonly carrierId = "us.usps"; + private config: USPSConfig | null = null; + private uspsUpstreamFetcher: USPSUpstreamFetcher | null = null; + + public async init( + input: CarrierInitInput & { config: USPSConfig } + ): Promise { + await super.init(input); + this.config = input.config; + this.uspsUpstreamFetcher = new USPSUpstreamFetcher({ + carrier: this, + originUpstreamFetcher: this.upstreamFetcher, + config: this.config, + }); + } + + public async track(input: CarrierTrackInput): Promise { + if (this.config == null) { + throw new Error("USPS is not initialized"); + } + if (this.uspsUpstreamFetcher == null) { + throw new Error("USPS is not initialized"); + } + + return await new USPSTrackScraper( + this.config, + this.uspsUpstreamFetcher, + input.trackingNumber + ).track(); + } +} + +class USPSTrackScraper { + private readonly logger: Logger; + + constructor( + readonly config: USPSConfig, + readonly upstreamFetcher: CarrierUpstreamFetcher, + readonly trackingNumber: string + ) { + this.logger = carrierLogger.child({ trackingNumber }); + } + + public async track(): Promise { + const traceResponse = await this.upstreamFetcher.fetch( + `https://api.usps.com/tracking/v3/tracking/${encodeURIComponent( + this.trackingNumber + )}?expand=SUMMARY` + ); + + const traceResponseJson: z.infer = + await traceResponse.json(); + this.logger.debug("traceResponseJson", { + traceResponseJson, + traceResponseStatus: traceResponse.status, + }); + + if (traceResponse.status === 400) { + throw new BadRequestError(); + } + + if (traceResponse.status === 404) { + throw new NotFoundError(); + } + + if (traceResponse.status !== 200) { + throw new InternalError(); + } + + const safeParseResult = + await USPSAPISchemas.TrackingSummary.strict().safeParseAsync( + traceResponseJson + ); + + if (!safeParseResult.success) { + this.logger.warn("response body parse failed (strict)", { + error: safeParseResult.error, + traceResponseJson, + }); + } + + // TODO : DETAIL 의 경우 time 데이터를 제대로 주지 않아 SUMMARY를 파싱 + + const events: TrackEvent[] = []; + for (const eventSummary of traceResponseJson.eventSummaries ?? []) { + events.unshift(this.transformEvent(eventSummary)); + } + + return { + events, + sender: null, + recipient: null, + carrierSpecificData: new Map(), + }; + } + + private transformEvent(eventSummary: string): TrackEvent { + return { + status: this.transformStatus(eventSummary), + time: this.transformTime(eventSummary), + location: null, + contact: null, + description: eventSummary, + carrierSpecificData: new Map(), + }; + } + + private transformStatus(eventSummary: string): TrackEventStatus { + const statusList: Array<[string, TrackEventStatusCode]> = [ + ["Shipping Label Created", TrackEventStatusCode.InformationReceived], + ["USPS in possession of item", TrackEventStatusCode.InTransit], + ["Departed Post Office", TrackEventStatusCode.InTransit], + ["Arrived at USPS Regional Origin Facility", TrackEventStatusCode.InTransit], + ["Departed USPS Regional Facility", TrackEventStatusCode.InTransit], + ["Arrived at USPS Facility", TrackEventStatusCode.InTransit], + ["Departed USPS Facility", TrackEventStatusCode.InTransit], + ["Arrived at USPS Regional Facility", TrackEventStatusCode.InTransit], + ["In Transit to Next Facility", TrackEventStatusCode.InTransit], + ["Processed Through USPS Regional Facility", TrackEventStatusCode.InTransit], + ["Processed Through Facility", TrackEventStatusCode.InTransit], + ["Processed through Facility", TrackEventStatusCode.InTransit], + ["Held in Customs", TrackEventStatusCode.InTransit], + ["Customs Clearance", TrackEventStatusCode.InTransit], + ["Acceptance", TrackEventStatusCode.InTransit], + ["Arrival at Post Office", TrackEventStatusCode.InTransit], + ["Arrived at Post Office", TrackEventStatusCode.InTransit], + ["Out for Delivery", TrackEventStatusCode.OutForDelivery], + ["Your item was delivered", TrackEventStatusCode.Delivered], + ["Arrived", TrackEventStatusCode.InTransit], + ["Departed", TrackEventStatusCode.InTransit], + ]; + + for (const status of statusList) { + if (eventSummary.startsWith(status[0])) { + return { + code: status[1], + name: status[0], + carrierSpecificData: new Map(), + }; + } + } + + this.logger.warn("Unexpected status", { + eventSummary, + }); + return { + code: TrackEventStatusCode.Unknown, + name: null, + carrierSpecificData: new Map(), + }; + } + + private transformTime(eventSummary: string): DateTime | null { + let match = eventSummary.match( + /(\d{1,2})\/(\d{1,2})\/(\d{4}), (\d{1,2}):(\d{2}) (am|pm)/ + ); + if (match !== null) { + const result = DateTime.fromObject( + { + month: Number(match[1]), + day: Number(match[2]), + year: Number(match[3]), + hour: (Number(match[4]) % 12) + (match[6] === "am" ? 0 : 12), + minute: Number(match[5]), + }, + { + zone: "America/New_York", + } + ); + if (!result.isValid) { + this.logger.warn("time parse error", { + eventSummary, + invalidReason: result.invalidReason, + }); + } + return result; + } + + match = eventSummary.match( + /(January|February|March|April|May|June|July|August|September|October|November|December) (\d{1,2}), (\d{4}), (\d{1,2}):(\d{2}) (am|pm)/ + ); + if (match !== null) { + const result = DateTime.fromObject( + { + month: this.monthLongToNumber(match[1]), + day: Number(match[2]), + year: Number(match[3]), + hour: (Number(match[4]) % 12) + (match[6] === "am" ? 0 : 12), + minute: Number(match[5]), + }, + { + zone: "America/New_York", + } + ); + if (!result.isValid) { + this.logger.warn("time parse error", { + eventSummary, + invalidReason: result.invalidReason, + }); + } + return result; + } + + match = eventSummary.match( + /(\d{1,2}):(\d{2}) (am|pm) on (January|February|March|April|May|June|July|August|September|October|November|December) (\d{1,2}), (\d{4})/ + ); + if (match !== null) { + const result = DateTime.fromObject( + { + hour: (Number(match[1]) % 12) + (match[3] === "am" ? 0 : 12), + minute: Number(match[2]), + month: this.monthLongToNumber(match[4]), + day: Number(match[5]), + year: Number(match[6]), + }, + { + zone: "America/New_York", + } + ); + if (!result.isValid) { + this.logger.warn("time parse error", { + eventSummary, + invalidReason: result.invalidReason, + }); + } + return result; + } + + this.logger.warn("not found time", { + eventSummary, + }); + return null; + } + + private monthLongToNumber(monthLong: string): number { + switch (monthLong) { + case "January": + return 1; + case "February": + return 2; + case "March": + return 3; + case "April": + return 4; + case "May": + return 5; + case "June": + return 6; + case "July": + return 7; + case "August": + return 8; + case "September": + return 9; + case "October": + return 10; + case "November": + return 11; + case "December": + return 12; + } + + throw new Error("month parse error"); + } +} + +export { USPS }; diff --git a/packages/core/src/core/errors.ts b/packages/core/src/core/errors.ts new file mode 100644 index 0000000..2d29e76 --- /dev/null +++ b/packages/core/src/core/errors.ts @@ -0,0 +1,22 @@ +abstract class TrackError extends Error { + public readonly isTrackError = true; + public abstract kind: string; + public abstract code: string; +} + +class BadRequestError extends TrackError { + public readonly kind: string = "BadRequestError"; + public readonly code: string = "BAD_REQUEST"; +} + +class NotFoundError extends TrackError { + public readonly kind: string = "NotFoundError"; + public readonly code: string = "NOT_FOUND"; +} + +class InternalError extends TrackError { + public readonly kind: string = "InternalError"; + public readonly code: string = "INTERNAL"; +} + +export { TrackError, BadRequestError, NotFoundError, InternalError }; diff --git a/packages/core/src/core/index.ts b/packages/core/src/core/index.ts new file mode 100644 index 0000000..a427c73 --- /dev/null +++ b/packages/core/src/core/index.ts @@ -0,0 +1 @@ +export * from "./interfaces"; diff --git a/packages/core/src/core/interfaces.ts b/packages/core/src/core/interfaces.ts new file mode 100644 index 0000000..661500e --- /dev/null +++ b/packages/core/src/core/interfaces.ts @@ -0,0 +1,118 @@ +import { z } from "zod"; +import { DateTime } from "luxon"; +import { parsePhoneNumber } from "libphonenumber-js"; +import { type CarrierUpstreamFetcher } from "../carrier-upstream-fetcher/CarrierUpstreamFetcher"; + +interface CarrierInitInput { + upstreamFetcher: CarrierUpstreamFetcher; +} + +abstract class Carrier { + protected upstreamFetcher!: CarrierUpstreamFetcher; + + public async init(input: CarrierInitInput & any): Promise { + this.upstreamFetcher = input.upstreamFetcher; + } + + public abstract track(input: CarrierTrackInput): Promise; + public abstract get carrierId(): string; +} + +interface CarrierTrackInput { + trackingNumber: string; +} + +const CarrierSpecificDataValueSchema = z.union([z.string(), z.number()]); +type CarrierSpecificDataValue = z.infer; + +const CarrierSpecificDataSchema = z.union([ + z.map(z.string(), CarrierSpecificDataValueSchema), + z + .record(CarrierSpecificDataValueSchema) + .transform((val) => new Map(Object.entries(val))), +]); +type CarrierSpecificData = z.infer; + +enum TrackEventStatusCode { + Unknown = "UNKNOWN", + InformationReceived = "INFORMATION_RECEIVED", + AtPickup = "AT_PICKUP", + InTransit = "IN_TRANSIT", + OutForDelivery = "OUT_FOR_DELIVERY", + AttemptFail = "ATTEMPT_FAIL", + Delivered = "DELIVERED", + AvailableForPickup = "AVAILABLE_FOR_PICKUP", + Exception = "EXCEPTION", +} + +const LocationSchema = z.object({ + /** ISO 3166-1 alpha-2 country code */ + countryCode: z.string().nullable(), + postalCode: z.string().nullable(), + name: z.string().nullable(), + carrierSpecificData: CarrierSpecificDataSchema, +}); +type Location = z.infer; + +const PhoneNumberSchema = z.string().transform((val) => parsePhoneNumber(val)); + +const MaskedPhoneNumberSchema = z.object({ + "@type": z.literal("@delivery-tracker/core/MaskedPhoneNumber"), + maskedPhoneNumber: z.string(), +}); +type MaskedPhoneNumber = z.infer; + +const ContactInfoSchema = z.object({ + name: z.string().nullable(), + location: LocationSchema.nullable(), + phoneNumber: z.union([PhoneNumberSchema, MaskedPhoneNumberSchema]).nullable(), + carrierSpecificData: CarrierSpecificDataSchema, +}); +type ContactInfo = z.infer; + +const TrackEventStatusSchema = z.object({ + code: z.nativeEnum(TrackEventStatusCode), + name: z.string().nullable(), + carrierSpecificData: CarrierSpecificDataSchema, +}); +type TrackEventStatus = z.infer; + +const TrackEventSchema = z.object({ + status: TrackEventStatusSchema, + time: z + .string() + .transform((val) => DateTime.fromISO(val, { setZone: true })) + .nullable(), + location: LocationSchema.nullable(), + contact: ContactInfoSchema.nullable(), + description: z.string().nullable(), + carrierSpecificData: CarrierSpecificDataSchema, +}); +type TrackEvent = z.infer; + +const TrackInfoSchema = z.object({ + events: z.array(TrackEventSchema), + sender: ContactInfoSchema.nullable(), + recipient: ContactInfoSchema.nullable(), + carrierSpecificData: CarrierSpecificDataSchema, +}); +type TrackInfo = z.infer; + +export { + Carrier, + type CarrierInitInput, + type CarrierTrackInput, + TrackInfoSchema, + type TrackInfo, + TrackEventStatusSchema, + type TrackEventStatus, + TrackEventSchema, + type TrackEvent, + TrackEventStatusCode, + LocationSchema, + type Location, + ContactInfoSchema, + type ContactInfo, + MaskedPhoneNumberSchema, + type MaskedPhoneNumber, +}; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts new file mode 100644 index 0000000..8bd65fd --- /dev/null +++ b/packages/core/src/index.ts @@ -0,0 +1,9 @@ +export * from "./core/interfaces"; +export * as errors from "./core/errors"; +export * as logger from "./logger"; +export type { CarrierRegistry } from "./carrier-registry/CarrierRegistry"; +export { DefaultCarrierRegistry } from "./carrier-registry/DefaultCarrierRegistry"; +export { + CarrierUpstreamFetcher, + type CarrierUpstreamFetcherInitInput, +} from "./carrier-upstream-fetcher/CarrierUpstreamFetcher"; diff --git a/packages/core/src/logger.ts b/packages/core/src/logger.ts new file mode 100644 index 0000000..17cbe6c --- /dev/null +++ b/packages/core/src/logger.ts @@ -0,0 +1,5 @@ +import * as winston from "winston"; + +const rootLogger = winston.createLogger(); + +export { rootLogger }; diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json new file mode 100644 index 0000000..487f261 --- /dev/null +++ b/packages/core/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist" + }, + "include": [ + "src/**/*" + ] +} \ No newline at end of file diff --git a/packages/server/package.json b/packages/server/package.json new file mode 100644 index 0000000..8630465 --- /dev/null +++ b/packages/server/package.json @@ -0,0 +1,25 @@ +{ + "name": "@delivery-tracker/server", + "private": true, + "type": "commonjs", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "scripts": { + "build": "tsc -p .", + "build-with-deps": "pnpm --filter '@delivery-tracker/api' --filter '@delivery-tracker/core' build-with-deps && pnpm build", + "start": "node dist/index.js", + "dev": "node-dev src/index.ts" + }, + "dependencies": { + "@apollo/server": "^4.7.0", + "@delivery-tracker/api": "workspace:*", + "@delivery-tracker/core": "workspace:*", + "luxon": "^3.4.0", + "winston": "^3.8.2" + }, + "devDependencies": { + "@types/luxon": "^3.3.1", + "node-dev": "^8.0.0", + "ts-node": "^10.9.1" + } +} \ No newline at end of file diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts new file mode 100644 index 0000000..43ac4be --- /dev/null +++ b/packages/server/src/index.ts @@ -0,0 +1,93 @@ +import type * as winston from "winston"; +import { ApolloServer } from "@apollo/server"; +import { startStandaloneServer } from "@apollo/server/standalone"; +import { + ApolloServerErrorCode, + unwrapResolverError, +} from "@apollo/server/errors"; +import { typeDefs, resolvers, type AppContext } from "@delivery-tracker/api"; +import { + DefaultCarrierRegistry, + logger as coreLogger, +} from "@delivery-tracker/core"; +import { initLogger } from "./logger"; + +const serverRootLogger: winston.Logger = coreLogger.rootLogger.child({ + module: "server", +}); + +const server = new ApolloServer({ + typeDefs, + resolvers: resolvers.resolvers, + formatError: (formattedError, error) => { + const extensions = formattedError.extensions ?? {}; + switch (extensions.code) { + case "INTERNAL": + case "BAD_REQUEST": + case "NOT_FOUND": + case ApolloServerErrorCode.INTERNAL_SERVER_ERROR: + extensions.code = "INTERNAL"; + break; + case ApolloServerErrorCode.GRAPHQL_PARSE_FAILED: + extensions.code = "BAD_REQUEST"; + break; + case ApolloServerErrorCode.GRAPHQL_VALIDATION_FAILED: + extensions.code = "BAD_REQUEST"; + break; + case ApolloServerErrorCode.PERSISTED_QUERY_NOT_FOUND: + extensions.code = "BAD_REQUEST"; + break; + case ApolloServerErrorCode.PERSISTED_QUERY_NOT_SUPPORTED: + extensions.code = "BAD_REQUEST"; + break; + case ApolloServerErrorCode.BAD_USER_INPUT: + extensions.code = "BAD_REQUEST"; + break; + case ApolloServerErrorCode.OPERATION_RESOLUTION_FAILURE: + extensions.code = "BAD_REQUEST"; + break; + default: + extensions.code = "INTERNAL"; + break; + } + + if (extensions.code === "INTERNAL") { + serverRootLogger.error("internal error response", { + formattedError, + error: unwrapResolverError(error), + }); + } + + return { + ...formattedError, + extensions, + message: + extensions.code === "INTERNAL" + ? "Internal error" + : formattedError.message, + }; + }, +}); + +async function main(): Promise { + const carrierRegistry = new DefaultCarrierRegistry(); + await carrierRegistry.init(); + + const appContext: AppContext = { + carrierRegistry, + }; + + const { url } = await startStandaloneServer(server, { + context: async ({ req, res }) => ({ + appContext, + }), + }); + serverRootLogger.info(`🚀 Server ready at ${url}`); +} + +initLogger(); +main().catch((err) => { + serverRootLogger.error("Uncaught error", { + error: err, + }); +}); diff --git a/packages/server/src/logger.ts b/packages/server/src/logger.ts new file mode 100644 index 0000000..1b468e5 --- /dev/null +++ b/packages/server/src/logger.ts @@ -0,0 +1,36 @@ +import * as winston from "winston"; +import { logger as coreLogger } from "@delivery-tracker/core"; +import { DateTime } from "luxon"; + +function initLogger(): void { + coreLogger.rootLogger.format = winston.format.combine( + winston.format.json({ + replacer: (key, value) => { + // luxon + if (value instanceof DateTime) { + return value.toISO(); + } + + // libphonenumber-js / PhoneNumber + if ( + typeof value === "object" && + value !== null && + "number" in value && + typeof value.number === "string" + ) { + return value.number; + } + return value; + }, + }) + ); + + if (process.env.LOG_LEVEL) { + coreLogger.rootLogger.level = process.env.LOG_LEVEL; + } + + // TODO : file mode + coreLogger.rootLogger.add(new winston.transports.Console()); +} + +export { initLogger }; diff --git a/packages/server/tsconfig.json b/packages/server/tsconfig.json new file mode 100644 index 0000000..f6a9e8f --- /dev/null +++ b/packages/server/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist" + }, +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..0753b88 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,7254 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@types/node': + specifier: ^18.16.0 + version: 18.16.0 + '@typescript-eslint/eslint-plugin': + specifier: ^5.59.0 + version: 5.59.0(@typescript-eslint/parser@5.59.0)(eslint@8.39.0)(typescript@5.0.4) + eslint: + specifier: ^8.39.0 + version: 8.39.0 + eslint-config-prettier: + specifier: ^8.8.0 + version: 8.8.0(eslint@8.39.0) + eslint-config-standard-with-typescript: + specifier: ^34.0.1 + version: 34.0.1(@typescript-eslint/eslint-plugin@5.59.0)(eslint-plugin-import@2.27.5)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.39.0)(typescript@5.0.4) + eslint-plugin-import: + specifier: ^2.27.5 + version: 2.27.5(@typescript-eslint/parser@5.59.0)(eslint@8.39.0) + eslint-plugin-n: + specifier: ^15.7.0 + version: 15.7.0(eslint@8.39.0) + eslint-plugin-prettier: + specifier: ^4.2.1 + version: 4.2.1(eslint-config-prettier@8.8.0)(eslint@8.39.0)(prettier@2.8.8) + eslint-plugin-promise: + specifier: ^6.1.1 + version: 6.1.1(eslint@8.39.0) + prettier: + specifier: ^2.8.8 + version: 2.8.8 + typescript: + specifier: ^5.0.4 + version: 5.0.4 + + packages/api: + dependencies: + '@delivery-tracker/core': + specifier: workspace:* + version: link:../core + graphql: + specifier: ^16.6.0 + version: 16.6.0 + winston: + specifier: ^3.8.2 + version: 3.8.2 + devDependencies: + '@graphql-codegen/cli': + specifier: ^3.3.1 + version: 3.3.1(@babel/core@7.21.5)(@types/node@18.16.0)(graphql@16.6.0) + '@graphql-codegen/typescript': + specifier: ^3.0.4 + version: 3.0.4(graphql@16.6.0) + '@graphql-codegen/typescript-resolvers': + specifier: ^3.2.1 + version: 3.2.1(graphql@16.6.0) + + packages/cli: + dependencies: + '@delivery-tracker/api': + specifier: workspace:* + version: link:../api + '@delivery-tracker/core': + specifier: workspace:* + version: link:../core + graphql: + specifier: ^16.6.0 + version: 16.6.0 + luxon: + specifier: ^3.4.0 + version: 3.4.0 + winston: + specifier: ^3.8.2 + version: 3.8.2 + yargs: + specifier: ^17.7.1 + version: 17.7.1 + devDependencies: + '@types/luxon': + specifier: ^3.3.1 + version: 3.3.1 + '@types/yargs': + specifier: ^17.0.24 + version: 17.0.24 + + packages/core: + dependencies: + cheerio: + specifier: 1.0.0-rc.12 + version: 1.0.0-rc.12 + iconv-lite: + specifier: ^0.6.3 + version: 0.6.3 + jsdom: + specifier: ^21.1.1 + version: 21.1.1 + libphonenumber-js: + specifier: ^1.10.28 + version: 1.10.28 + luxon: + specifier: ^3.4.0 + version: 3.4.0 + tough-cookie: + specifier: ^4.1.2 + version: 4.1.2 + winston: + specifier: ^3.8.2 + version: 3.8.2 + yaml: + specifier: ^2.3.1 + version: 2.3.1 + zod: + specifier: ^3.21.4 + version: 3.21.4 + devDependencies: + '@jest/globals': + specifier: ^29.6.2 + version: 29.6.2 + '@types/jsdom': + specifier: ^21.1.1 + version: 21.1.1 + '@types/luxon': + specifier: ^3.3.1 + version: 3.3.1 + '@types/tough-cookie': + specifier: ^4.0.2 + version: 4.0.2 + jest: + specifier: ^29.6.2 + version: 29.6.2(@types/node@18.16.0)(ts-node@10.9.1) + ts-jest: + specifier: ^29.1.1 + version: 29.1.1(@babel/core@7.21.5)(jest@29.6.2)(typescript@5.1.6) + ts-node: + specifier: ^10.9.1 + version: 10.9.1(@types/node@18.16.0)(typescript@5.1.6) + typescript: + specifier: ^5.1.6 + version: 5.1.6 + + packages/server: + dependencies: + '@apollo/server': + specifier: ^4.7.0 + version: 4.7.0(graphql@16.6.0) + '@delivery-tracker/api': + specifier: workspace:* + version: link:../api + '@delivery-tracker/core': + specifier: workspace:* + version: link:../core + luxon: + specifier: ^3.4.0 + version: 3.4.0 + winston: + specifier: ^3.8.2 + version: 3.8.2 + devDependencies: + '@types/luxon': + specifier: ^3.3.1 + version: 3.3.1 + node-dev: + specifier: ^8.0.0 + version: 8.0.0 + ts-node: + specifier: ^10.9.1 + version: 10.9.1(@types/node@18.16.0)(typescript@5.1.6) + +packages: + + /@ampproject/remapping@2.2.1: + resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.18 + dev: true + + /@apollo/cache-control-types@1.0.2(graphql@16.6.0): + resolution: {integrity: sha512-Por80co1eUm4ATsvjCOoS/tIR8PHxqVjsA6z76I6Vw0rFn4cgyVElQcmQDIZiYsy41k8e5xkrMRECkM2WR8pNw==} + peerDependencies: + graphql: 14.x || 15.x || 16.x + dependencies: + graphql: 16.6.0 + dev: false + + /@apollo/protobufjs@1.2.7: + resolution: {integrity: sha512-Lahx5zntHPZia35myYDBRuF58tlwPskwHc5CWBZC/4bMKB6siTBWwtMrkqXcsNwQiFSzSx5hKdRPUmemrEp3Gg==} + hasBin: true + requiresBuild: true + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/long': 4.0.2 + long: 4.0.0 + dev: false + + /@apollo/server-gateway-interface@1.1.0(graphql@16.6.0): + resolution: {integrity: sha512-0rhG++QtGfr4YhhIHgxZ9BdMFthaPY6LbhI9Au90osbfLMiZ7f8dmZsEX1mp7O1h8MJwCu6Dp0I/KcGbSvfUGA==} + peerDependencies: + graphql: 14.x || 15.x || 16.x + dependencies: + '@apollo/usage-reporting-protobuf': 4.1.0 + '@apollo/utils.fetcher': 2.0.1 + '@apollo/utils.keyvaluecache': 2.1.1 + '@apollo/utils.logger': 2.0.1 + graphql: 16.6.0 + dev: false + + /@apollo/server@4.7.0(graphql@16.6.0): + resolution: {integrity: sha512-qPfMnsHHA+U/F3dRAAlyiXWbTjM3U/KoMtS95OrfYKWXbeenW9R82nZISrI3D0cBIYICigZC3DrQVl9M5mYA+g==} + engines: {node: '>=14.16.0'} + peerDependencies: + graphql: ^16.6.0 + dependencies: + '@apollo/cache-control-types': 1.0.2(graphql@16.6.0) + '@apollo/server-gateway-interface': 1.1.0(graphql@16.6.0) + '@apollo/usage-reporting-protobuf': 4.1.0 + '@apollo/utils.createhash': 2.0.1 + '@apollo/utils.fetcher': 2.0.1 + '@apollo/utils.isnodelike': 2.0.1 + '@apollo/utils.keyvaluecache': 2.1.1 + '@apollo/utils.logger': 2.0.1 + '@apollo/utils.usagereporting': 2.0.1(graphql@16.6.0) + '@apollo/utils.withrequired': 2.0.1 + '@graphql-tools/schema': 9.0.19(graphql@16.6.0) + '@josephg/resolvable': 1.0.1 + '@types/express': 4.17.17 + '@types/express-serve-static-core': 4.17.33 + '@types/node-fetch': 2.6.3 + async-retry: 1.3.3 + body-parser: 1.20.2 + cors: 2.8.5 + express: 4.18.2 + graphql: 16.6.0 + loglevel: 1.8.1 + lru-cache: 7.18.3 + negotiator: 0.6.3 + node-abort-controller: 3.1.1 + node-fetch: 2.6.9 + uuid: 9.0.0 + whatwg-mimetype: 3.0.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + + /@apollo/usage-reporting-protobuf@4.1.0: + resolution: {integrity: sha512-hXouMuw5pQVkzi8dgMybmr6Y11+eRmMQVoB5TF0HyTwAg9SOq/v3OCuiYqcVUKdBcskU9Msp+XvjAk0GKpWCwQ==} + dependencies: + '@apollo/protobufjs': 1.2.7 + dev: false + + /@apollo/utils.createhash@2.0.1: + resolution: {integrity: sha512-fQO4/ZOP8LcXWvMNhKiee+2KuKyqIcfHrICA+M4lj/h/Lh1H10ICcUtk6N/chnEo5HXu0yejg64wshdaiFitJg==} + engines: {node: '>=14'} + dependencies: + '@apollo/utils.isnodelike': 2.0.1 + sha.js: 2.4.11 + dev: false + + /@apollo/utils.dropunuseddefinitions@2.0.1(graphql@16.6.0): + resolution: {integrity: sha512-EsPIBqsSt2BwDsv8Wu76LK5R1KtsVkNoO4b0M5aK0hx+dGg9xJXuqlr7Fo34Dl+y83jmzn+UvEW+t1/GP2melA==} + engines: {node: '>=14'} + peerDependencies: + graphql: 14.x || 15.x || 16.x + dependencies: + graphql: 16.6.0 + dev: false + + /@apollo/utils.fetcher@2.0.1: + resolution: {integrity: sha512-jvvon885hEyWXd4H6zpWeN3tl88QcWnHp5gWF5OPF34uhvoR+DFqcNxs9vrRaBBSY3qda3Qe0bdud7tz2zGx1A==} + engines: {node: '>=14'} + dev: false + + /@apollo/utils.isnodelike@2.0.1: + resolution: {integrity: sha512-w41XyepR+jBEuVpoRM715N2ZD0xMD413UiJx8w5xnAZD2ZkSJnMJBoIzauK83kJpSgNuR6ywbV29jG9NmxjK0Q==} + engines: {node: '>=14'} + dev: false + + /@apollo/utils.keyvaluecache@2.1.1: + resolution: {integrity: sha512-qVo5PvUUMD8oB9oYvq4ViCjYAMWnZ5zZwEjNF37L2m1u528x5mueMlU+Cr1UinupCgdB78g+egA1G98rbJ03Vw==} + engines: {node: '>=14'} + dependencies: + '@apollo/utils.logger': 2.0.1 + lru-cache: 7.18.3 + dev: false + + /@apollo/utils.logger@2.0.1: + resolution: {integrity: sha512-YuplwLHaHf1oviidB7MxnCXAdHp3IqYV8n0momZ3JfLniae92eYqMIx+j5qJFX6WKJPs6q7bczmV4lXIsTu5Pg==} + engines: {node: '>=14'} + dev: false + + /@apollo/utils.printwithreducedwhitespace@2.0.1(graphql@16.6.0): + resolution: {integrity: sha512-9M4LUXV/fQBh8vZWlLvb/HyyhjJ77/I5ZKu+NBWV/BmYGyRmoEP9EVAy7LCVoY3t8BDcyCAGfxJaLFCSuQkPUg==} + engines: {node: '>=14'} + peerDependencies: + graphql: 14.x || 15.x || 16.x + dependencies: + graphql: 16.6.0 + dev: false + + /@apollo/utils.removealiases@2.0.1(graphql@16.6.0): + resolution: {integrity: sha512-0joRc2HBO4u594Op1nev+mUF6yRnxoUH64xw8x3bX7n8QBDYdeYgY4tF0vJReTy+zdn2xv6fMsquATSgC722FA==} + engines: {node: '>=14'} + peerDependencies: + graphql: 14.x || 15.x || 16.x + dependencies: + graphql: 16.6.0 + dev: false + + /@apollo/utils.sortast@2.0.1(graphql@16.6.0): + resolution: {integrity: sha512-eciIavsWpJ09za1pn37wpsCGrQNXUhM0TktnZmHwO+Zy9O4fu/WdB4+5BvVhFiZYOXvfjzJUcc+hsIV8RUOtMw==} + engines: {node: '>=14'} + peerDependencies: + graphql: 14.x || 15.x || 16.x + dependencies: + graphql: 16.6.0 + lodash.sortby: 4.7.0 + dev: false + + /@apollo/utils.stripsensitiveliterals@2.0.1(graphql@16.6.0): + resolution: {integrity: sha512-QJs7HtzXS/JIPMKWimFnUMK7VjkGQTzqD9bKD1h3iuPAqLsxd0mUNVbkYOPTsDhUKgcvUOfOqOJWYohAKMvcSA==} + engines: {node: '>=14'} + peerDependencies: + graphql: 14.x || 15.x || 16.x + dependencies: + graphql: 16.6.0 + dev: false + + /@apollo/utils.usagereporting@2.0.1(graphql@16.6.0): + resolution: {integrity: sha512-18smkNfiSfu5yj2mpCIfSzmpDNh90a4PQ6t8kSwGKcPRD3KD83TfK7fF37fSRdnvO93dBkGreWisLXnCpqfWXg==} + engines: {node: '>=14'} + peerDependencies: + graphql: 14.x || 15.x || 16.x + dependencies: + '@apollo/usage-reporting-protobuf': 4.1.0 + '@apollo/utils.dropunuseddefinitions': 2.0.1(graphql@16.6.0) + '@apollo/utils.printwithreducedwhitespace': 2.0.1(graphql@16.6.0) + '@apollo/utils.removealiases': 2.0.1(graphql@16.6.0) + '@apollo/utils.sortast': 2.0.1(graphql@16.6.0) + '@apollo/utils.stripsensitiveliterals': 2.0.1(graphql@16.6.0) + graphql: 16.6.0 + dev: false + + /@apollo/utils.withrequired@2.0.1: + resolution: {integrity: sha512-YBDiuAX9i1lLc6GeTy1m7DGLFn/gMnvXqlalOIMjM7DeOgIacEjjfwPqb0M1CQ2v11HhR15d1NmxJoRCfrNqcA==} + engines: {node: '>=14'} + dev: false + + /@ardatan/relay-compiler@12.0.0(graphql@16.6.0): + resolution: {integrity: sha512-9anThAaj1dQr6IGmzBMcfzOQKTa5artjuPmw8NYK/fiGEMjADbSguBY2FMDykt+QhilR3wc9VA/3yVju7JHg7Q==} + hasBin: true + peerDependencies: + graphql: '*' + dependencies: + '@babel/core': 7.21.5 + '@babel/generator': 7.21.5 + '@babel/parser': 7.21.5 + '@babel/runtime': 7.21.5 + '@babel/traverse': 7.21.5 + '@babel/types': 7.21.5 + babel-preset-fbjs: 3.4.0(@babel/core@7.21.5) + chalk: 4.1.2 + fb-watchman: 2.0.2 + fbjs: 3.0.4 + glob: 7.2.3 + graphql: 16.6.0 + immutable: 3.7.6 + invariant: 2.2.4 + nullthrows: 1.1.1 + relay-runtime: 12.0.0 + signedsource: 1.0.0 + yargs: 15.4.1 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + + /@ardatan/sync-fetch@0.0.1: + resolution: {integrity: sha512-xhlTqH0m31mnsG0tIP4ETgfSB6gXDaYYsUWTrlUV93fFQPI9dd8hE0Ot6MHLCtqgB32hwJAC3YZMWlXZw7AleA==} + engines: {node: '>=14'} + dependencies: + node-fetch: 2.6.9 + transitivePeerDependencies: + - encoding + dev: true + + /@babel/code-frame@7.21.4: + resolution: {integrity: sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.18.6 + dev: true + + /@babel/compat-data@7.21.7: + resolution: {integrity: sha512-KYMqFYTaenzMK4yUtf4EW9wc4N9ef80FsbMtkwool5zpwl4YrT1SdWYSTRcT94KO4hannogdS+LxY7L+arP3gA==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/core@7.21.5: + resolution: {integrity: sha512-9M398B/QH5DlfCOTKDZT1ozXr0x8uBEeFd+dJraGUZGiaNpGCDVGCc14hZexsMblw3XxltJ+6kSvogp9J+5a9g==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.1 + '@babel/code-frame': 7.21.4 + '@babel/generator': 7.21.5 + '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.21.5) + '@babel/helper-module-transforms': 7.21.5 + '@babel/helpers': 7.21.5 + '@babel/parser': 7.21.5 + '@babel/template': 7.20.7 + '@babel/traverse': 7.21.5 + '@babel/types': 7.21.5 + convert-source-map: 1.9.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/generator@7.21.5: + resolution: {integrity: sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.5 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.18 + jsesc: 2.5.2 + dev: true + + /@babel/helper-annotate-as-pure@7.18.6: + resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.5 + dev: true + + /@babel/helper-compilation-targets@7.21.5(@babel/core@7.21.5): + resolution: {integrity: sha512-1RkbFGUKex4lvsB9yhIfWltJM5cZKUftB2eNajaDv3dCMEp49iBG0K14uH8NnX9IPux2+mK7JGEOB0jn48/J6w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/compat-data': 7.21.7 + '@babel/core': 7.21.5 + '@babel/helper-validator-option': 7.21.0 + browserslist: 4.21.5 + lru-cache: 5.1.1 + semver: 6.3.0 + dev: true + + /@babel/helper-create-class-features-plugin@7.21.5(@babel/core@7.21.5): + resolution: {integrity: sha512-yNSEck9SuDvPTEUYm4BSXl6ZVC7yO5ZLEMAhG3v3zi7RDxyL/nQDemWWZmw4L0stPWwhpnznRRyJHPRcbXR2jw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-environment-visitor': 7.21.5 + '@babel/helper-function-name': 7.21.0 + '@babel/helper-member-expression-to-functions': 7.21.5 + '@babel/helper-optimise-call-expression': 7.18.6 + '@babel/helper-replace-supers': 7.21.5 + '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 + '@babel/helper-split-export-declaration': 7.18.6 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-environment-visitor@7.21.5: + resolution: {integrity: sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-function-name@7.21.0: + resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.20.7 + '@babel/types': 7.21.5 + dev: true + + /@babel/helper-hoist-variables@7.18.6: + resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.5 + dev: true + + /@babel/helper-member-expression-to-functions@7.21.5: + resolution: {integrity: sha512-nIcGfgwpH2u4n9GG1HpStW5Ogx7x7ekiFHbjjFRKXbn5zUvqO9ZgotCO4x1aNbKn/x/xOUaXEhyNHCwtFCpxWg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.5 + dev: true + + /@babel/helper-module-imports@7.21.4: + resolution: {integrity: sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.5 + dev: true + + /@babel/helper-module-transforms@7.21.5: + resolution: {integrity: sha512-bI2Z9zBGY2q5yMHoBvJ2a9iX3ZOAzJPm7Q8Yz6YeoUjU/Cvhmi2G4QyTNyPBqqXSgTjUxRg3L0xV45HvkNWWBw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-environment-visitor': 7.21.5 + '@babel/helper-module-imports': 7.21.4 + '@babel/helper-simple-access': 7.21.5 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/helper-validator-identifier': 7.19.1 + '@babel/template': 7.20.7 + '@babel/traverse': 7.21.5 + '@babel/types': 7.21.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-optimise-call-expression@7.18.6: + resolution: {integrity: sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.5 + dev: true + + /@babel/helper-plugin-utils@7.21.5: + resolution: {integrity: sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-plugin-utils@7.22.5: + resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-replace-supers@7.21.5: + resolution: {integrity: sha512-/y7vBgsr9Idu4M6MprbOVUfH3vs7tsIfnVWv/Ml2xgwvyH6LTngdfbf5AdsKwkJy4zgy1X/kuNrEKvhhK28Yrg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-environment-visitor': 7.21.5 + '@babel/helper-member-expression-to-functions': 7.21.5 + '@babel/helper-optimise-call-expression': 7.18.6 + '@babel/template': 7.20.7 + '@babel/traverse': 7.21.5 + '@babel/types': 7.21.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-simple-access@7.21.5: + resolution: {integrity: sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.5 + dev: true + + /@babel/helper-skip-transparent-expression-wrappers@7.20.0: + resolution: {integrity: sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.5 + dev: true + + /@babel/helper-split-export-declaration@7.18.6: + resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.5 + dev: true + + /@babel/helper-string-parser@7.21.5: + resolution: {integrity: sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-identifier@7.19.1: + resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-option@7.21.0: + resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helpers@7.21.5: + resolution: {integrity: sha512-BSY+JSlHxOmGsPTydUkPf1MdMQ3M81x5xGCOVgWM3G8XH77sJ292Y2oqcp0CbbgxhqBuI46iUz1tT7hqP7EfgA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.20.7 + '@babel/traverse': 7.21.5 + '@babel/types': 7.21.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/highlight@7.18.6: + resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.19.1 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@babel/parser@7.21.5: + resolution: {integrity: sha512-J+IxH2IsxV4HbnTrSWgMAQj0UEo61hDA4Ny8h8PCX0MLXiibqHbqIOVneqdocemSBc22VpBKxt4J6FQzy9HarQ==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.21.5 + dev: true + + /@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.21.5): + resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-create-class-features-plugin': 7.21.5(@babel/core@7.21.5) + '@babel/helper-plugin-utils': 7.21.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.21.5): + resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.21.7 + '@babel/core': 7.21.5 + '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.21.5) + '@babel/helper-plugin-utils': 7.21.5 + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.5) + '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.21.5) + dev: true + + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.21.5): + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.21.5): + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.21.5): + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.21.5 + dev: true + + /@babel/plugin-syntax-flow@7.21.4(@babel/core@7.21.5): + resolution: {integrity: sha512-l9xd3N+XG4fZRxEP3vXdK6RW7vN1Uf5dxzRC/09wV86wqZ/YYQooBIGNsiRdfNR3/q2/5pPzV4B54J/9ctX5jw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.21.5 + dev: true + + /@babel/plugin-syntax-import-assertions@7.20.0(@babel/core@7.21.5): + resolution: {integrity: sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.21.5 + dev: true + + /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.21.5): + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.21.5): + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-jsx@7.21.4(@babel/core@7.21.5): + resolution: {integrity: sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.21.5 + dev: true + + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.21.5): + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.21.5): + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.21.5): + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.21.5): + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.21.5 + dev: true + + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.21.5): + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.21.5): + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.21.5): + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-typescript@7.22.5(@babel/core@7.21.5): + resolution: {integrity: sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-arrow-functions@7.21.5(@babel/core@7.21.5): + resolution: {integrity: sha512-wb1mhwGOCaXHDTcsRYMKF9e5bbMgqwxtqa2Y1ifH96dXJPwbuLX9qHy3clhrxVqgMz7nyNXs8VkxdH8UBcjKqA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.21.5 + dev: true + + /@babel/plugin-transform-block-scoped-functions@7.18.6(@babel/core@7.21.5): + resolution: {integrity: sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.21.5 + dev: true + + /@babel/plugin-transform-block-scoping@7.21.0(@babel/core@7.21.5): + resolution: {integrity: sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.21.5 + dev: true + + /@babel/plugin-transform-classes@7.21.0(@babel/core@7.21.5): + resolution: {integrity: sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.21.5) + '@babel/helper-environment-visitor': 7.21.5 + '@babel/helper-function-name': 7.21.0 + '@babel/helper-optimise-call-expression': 7.18.6 + '@babel/helper-plugin-utils': 7.21.5 + '@babel/helper-replace-supers': 7.21.5 + '@babel/helper-split-export-declaration': 7.18.6 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-computed-properties@7.21.5(@babel/core@7.21.5): + resolution: {integrity: sha512-TR653Ki3pAwxBxUe8srfF3e4Pe3FTA46uaNHYyQwIoM4oWKSoOZiDNyHJ0oIoDIUPSRQbQG7jzgVBX3FPVne1Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.21.5 + '@babel/template': 7.20.7 + dev: true + + /@babel/plugin-transform-destructuring@7.21.3(@babel/core@7.21.5): + resolution: {integrity: sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.21.5 + dev: true + + /@babel/plugin-transform-flow-strip-types@7.21.0(@babel/core@7.21.5): + resolution: {integrity: sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.21.5 + '@babel/plugin-syntax-flow': 7.21.4(@babel/core@7.21.5) + dev: true + + /@babel/plugin-transform-for-of@7.21.5(@babel/core@7.21.5): + resolution: {integrity: sha512-nYWpjKW/7j/I/mZkGVgHJXh4bA1sfdFnJoOXwJuj4m3Q2EraO/8ZyrkCau9P5tbHQk01RMSt6KYLCsW7730SXQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.21.5 + dev: true + + /@babel/plugin-transform-function-name@7.18.9(@babel/core@7.21.5): + resolution: {integrity: sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.21.5) + '@babel/helper-function-name': 7.21.0 + '@babel/helper-plugin-utils': 7.21.5 + dev: true + + /@babel/plugin-transform-literals@7.18.9(@babel/core@7.21.5): + resolution: {integrity: sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.21.5 + dev: true + + /@babel/plugin-transform-member-expression-literals@7.18.6(@babel/core@7.21.5): + resolution: {integrity: sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.21.5 + dev: true + + /@babel/plugin-transform-modules-commonjs@7.21.5(@babel/core@7.21.5): + resolution: {integrity: sha512-OVryBEgKUbtqMoB7eG2rs6UFexJi6Zj6FDXx+esBLPTCxCNxAY9o+8Di7IsUGJ+AVhp5ncK0fxWUBd0/1gPhrQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-module-transforms': 7.21.5 + '@babel/helper-plugin-utils': 7.21.5 + '@babel/helper-simple-access': 7.21.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-object-super@7.18.6(@babel/core@7.21.5): + resolution: {integrity: sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.21.5 + '@babel/helper-replace-supers': 7.21.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-parameters@7.21.3(@babel/core@7.21.5): + resolution: {integrity: sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.21.5 + dev: true + + /@babel/plugin-transform-property-literals@7.18.6(@babel/core@7.21.5): + resolution: {integrity: sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.21.5 + dev: true + + /@babel/plugin-transform-react-display-name@7.18.6(@babel/core@7.21.5): + resolution: {integrity: sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.21.5 + dev: true + + /@babel/plugin-transform-react-jsx@7.21.5(@babel/core@7.21.5): + resolution: {integrity: sha512-ELdlq61FpoEkHO6gFRpfj0kUgSwQTGoaEU8eMRoS8Dv3v6e7BjEAj5WMtIBRdHUeAioMhKP5HyxNzNnP+heKbA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-module-imports': 7.21.4 + '@babel/helper-plugin-utils': 7.21.5 + '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.21.5) + '@babel/types': 7.21.5 + dev: true + + /@babel/plugin-transform-shorthand-properties@7.18.6(@babel/core@7.21.5): + resolution: {integrity: sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.21.5 + dev: true + + /@babel/plugin-transform-spread@7.20.7(@babel/core@7.21.5): + resolution: {integrity: sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.21.5 + '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 + dev: true + + /@babel/plugin-transform-template-literals@7.18.9(@babel/core@7.21.5): + resolution: {integrity: sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.5 + '@babel/helper-plugin-utils': 7.21.5 + dev: true + + /@babel/runtime@7.21.5: + resolution: {integrity: sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.13.11 + dev: true + + /@babel/template@7.20.7: + resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.21.4 + '@babel/parser': 7.21.5 + '@babel/types': 7.21.5 + dev: true + + /@babel/traverse@7.21.5: + resolution: {integrity: sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.21.4 + '@babel/generator': 7.21.5 + '@babel/helper-environment-visitor': 7.21.5 + '@babel/helper-function-name': 7.21.0 + '@babel/helper-hoist-variables': 7.18.6 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/parser': 7.21.5 + '@babel/types': 7.21.5 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/types@7.21.5: + resolution: {integrity: sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.21.5 + '@babel/helper-validator-identifier': 7.19.1 + to-fast-properties: 2.0.0 + dev: true + + /@bcoe/v8-coverage@0.2.3: + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + dev: true + + /@colors/colors@1.5.0: + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + dev: false + + /@cspotcode/source-map-support@0.8.1: + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + dev: true + + /@dabh/diagnostics@2.0.3: + resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + dependencies: + colorspace: 1.1.4 + enabled: 2.0.0 + kuler: 2.0.0 + dev: false + + /@eslint-community/eslint-utils@4.4.0(eslint@8.39.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.39.0 + eslint-visitor-keys: 3.4.0 + dev: true + + /@eslint-community/regexpp@4.5.0: + resolution: {integrity: sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@2.0.2: + resolution: {integrity: sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.5.1 + globals: 13.20.0 + ignore: 5.2.4 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@8.39.0: + resolution: {integrity: sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@graphql-codegen/cli@3.3.1(@babel/core@7.21.5)(@types/node@18.16.0)(graphql@16.6.0): + resolution: {integrity: sha512-4Es8Y9zFeT0Zx2qRL7L3qXDbbqvXK6aID+8v8lP6gaYD+uWx3Jd4Hsq5vxwVBR+6flm0BW/C85Qm0cvmT7O6LA==} + hasBin: true + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@babel/generator': 7.21.5 + '@babel/template': 7.20.7 + '@babel/types': 7.21.5 + '@graphql-codegen/core': 3.1.0(graphql@16.6.0) + '@graphql-codegen/plugin-helpers': 4.2.0(graphql@16.6.0) + '@graphql-tools/apollo-engine-loader': 7.3.26(graphql@16.6.0) + '@graphql-tools/code-file-loader': 7.3.23(@babel/core@7.21.5)(graphql@16.6.0) + '@graphql-tools/git-loader': 7.2.22(@babel/core@7.21.5)(graphql@16.6.0) + '@graphql-tools/github-loader': 7.3.28(@babel/core@7.21.5)(@types/node@18.16.0)(graphql@16.6.0) + '@graphql-tools/graphql-file-loader': 7.5.17(graphql@16.6.0) + '@graphql-tools/json-file-loader': 7.4.18(graphql@16.6.0) + '@graphql-tools/load': 7.8.14(graphql@16.6.0) + '@graphql-tools/prisma-loader': 7.2.71(@types/node@18.16.0)(graphql@16.6.0) + '@graphql-tools/url-loader': 7.17.18(@types/node@18.16.0)(graphql@16.6.0) + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + '@parcel/watcher': 2.1.0 + '@whatwg-node/fetch': 0.8.8 + chalk: 4.1.2 + cosmiconfig: 7.1.0 + debounce: 1.2.1 + detect-indent: 6.1.0 + graphql: 16.6.0 + graphql-config: 4.5.0(@types/node@18.16.0)(graphql@16.6.0) + inquirer: 8.2.5 + is-glob: 4.0.3 + jiti: 1.18.2 + json-to-pretty-yaml: 1.2.2 + listr2: 4.0.5 + log-symbols: 4.1.0 + micromatch: 4.0.5 + shell-quote: 1.8.1 + string-env-interpolation: 1.0.1 + ts-log: 2.2.5 + tslib: 2.5.0 + yaml: 1.10.2 + yargs: 17.7.1 + transitivePeerDependencies: + - '@babel/core' + - '@types/node' + - bufferutil + - cosmiconfig-toml-loader + - encoding + - enquirer + - supports-color + - utf-8-validate + dev: true + + /@graphql-codegen/core@3.1.0(graphql@16.6.0): + resolution: {integrity: sha512-DH1/yaR7oJE6/B+c6ZF2Tbdh7LixF1K8L+8BoSubjNyQ8pNwR4a70mvc1sv6H7qgp6y1bPQ9tKE+aazRRshysw==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-codegen/plugin-helpers': 4.2.0(graphql@16.6.0) + '@graphql-tools/schema': 9.0.19(graphql@16.6.0) + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + graphql: 16.6.0 + tslib: 2.5.0 + dev: true + + /@graphql-codegen/plugin-helpers@4.2.0(graphql@16.6.0): + resolution: {integrity: sha512-THFTCfg+46PXlXobYJ/OoCX6pzjI+9woQqCjdyKtgoI0tn3Xq2HUUCiidndxUpEYVrXb5pRiRXb7b/ZbMQqD0A==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + change-case-all: 1.0.15 + common-tags: 1.8.2 + graphql: 16.6.0 + import-from: 4.0.0 + lodash: 4.17.21 + tslib: 2.5.0 + dev: true + + /@graphql-codegen/schema-ast@3.0.1(graphql@16.6.0): + resolution: {integrity: sha512-rTKTi4XiW4QFZnrEqetpiYEWVsOFNoiR/v3rY9mFSttXFbIwNXPme32EspTiGWmEEdHY8UuTDtZN3vEcs/31zw==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-codegen/plugin-helpers': 4.2.0(graphql@16.6.0) + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + graphql: 16.6.0 + tslib: 2.5.0 + dev: true + + /@graphql-codegen/typescript-resolvers@3.2.1(graphql@16.6.0): + resolution: {integrity: sha512-2ZIHk5J6HTuylse5ZIxw+aega54prHxvj7vM8hiKJ6vejZ94kvVPAq4aWmSFOkZ5lqU3YnM/ZyWfnhT5CUDj1g==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-codegen/plugin-helpers': 4.2.0(graphql@16.6.0) + '@graphql-codegen/typescript': 3.0.4(graphql@16.6.0) + '@graphql-codegen/visitor-plugin-common': 3.1.1(graphql@16.6.0) + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + auto-bind: 4.0.0 + graphql: 16.6.0 + tslib: 2.5.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + + /@graphql-codegen/typescript@3.0.4(graphql@16.6.0): + resolution: {integrity: sha512-x4O47447DZrWNtE/l5CU9QzzW4m1RbmCEdijlA3s2flG/y1Ckqdemob4CWfilSm5/tZ3w1junVDY616RDTSvZw==} + peerDependencies: + graphql: ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-codegen/plugin-helpers': 4.2.0(graphql@16.6.0) + '@graphql-codegen/schema-ast': 3.0.1(graphql@16.6.0) + '@graphql-codegen/visitor-plugin-common': 3.1.1(graphql@16.6.0) + auto-bind: 4.0.0 + graphql: 16.6.0 + tslib: 2.5.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + + /@graphql-codegen/visitor-plugin-common@3.1.1(graphql@16.6.0): + resolution: {integrity: sha512-uAfp+zu/009R3HUAuTK2AamR1bxIltM6rrYYI6EXSmkM3rFtFsLTuJhjUDj98HcUCszJZrADppz8KKLGRUVlNg==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-codegen/plugin-helpers': 4.2.0(graphql@16.6.0) + '@graphql-tools/optimize': 1.4.0(graphql@16.6.0) + '@graphql-tools/relay-operation-optimizer': 6.5.18(graphql@16.6.0) + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + auto-bind: 4.0.0 + change-case-all: 1.0.15 + dependency-graph: 0.11.0 + graphql: 16.6.0 + graphql-tag: 2.12.6(graphql@16.6.0) + parse-filepath: 1.0.2 + tslib: 2.5.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + + /@graphql-tools/apollo-engine-loader@7.3.26(graphql@16.6.0): + resolution: {integrity: sha512-h1vfhdJFjnCYn9b5EY1Z91JTF0KB3hHVJNQIsiUV2mpQXZdeOXQoaWeYEKaiI5R6kwBw5PP9B0fv3jfUIG8LyQ==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@ardatan/sync-fetch': 0.0.1 + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + '@whatwg-node/fetch': 0.8.8 + graphql: 16.6.0 + tslib: 2.5.0 + transitivePeerDependencies: + - encoding + dev: true + + /@graphql-tools/batch-execute@8.5.22(graphql@16.6.0): + resolution: {integrity: sha512-hcV1JaY6NJQFQEwCKrYhpfLK8frSXDbtNMoTur98u10Cmecy1zrqNKSqhEyGetpgHxaJRqszGzKeI3RuroDN6A==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + dataloader: 2.2.2 + graphql: 16.6.0 + tslib: 2.5.0 + value-or-promise: 1.0.12 + dev: true + + /@graphql-tools/code-file-loader@7.3.23(@babel/core@7.21.5)(graphql@16.6.0): + resolution: {integrity: sha512-8Wt1rTtyTEs0p47uzsPJ1vAtfAx0jmxPifiNdmo9EOCuUPyQGEbMaik/YkqZ7QUFIEYEQu+Vgfo8tElwOPtx5Q==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/graphql-tag-pluck': 7.5.2(@babel/core@7.21.5)(graphql@16.6.0) + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + globby: 11.1.0 + graphql: 16.6.0 + tslib: 2.5.0 + unixify: 1.0.0 + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + + /@graphql-tools/delegate@9.0.35(graphql@16.6.0): + resolution: {integrity: sha512-jwPu8NJbzRRMqi4Vp/5QX1vIUeUPpWmlQpOkXQD2r1X45YsVceyUUBnktCrlJlDB4jPRVy7JQGwmYo3KFiOBMA==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/batch-execute': 8.5.22(graphql@16.6.0) + '@graphql-tools/executor': 0.0.20(graphql@16.6.0) + '@graphql-tools/schema': 9.0.19(graphql@16.6.0) + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + dataloader: 2.2.2 + graphql: 16.6.0 + tslib: 2.5.0 + value-or-promise: 1.0.12 + dev: true + + /@graphql-tools/executor-graphql-ws@0.0.14(graphql@16.6.0): + resolution: {integrity: sha512-P2nlkAsPZKLIXImFhj0YTtny5NQVGSsKnhi7PzXiaHSXc6KkzqbWZHKvikD4PObanqg+7IO58rKFpGXP7eeO+w==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + '@repeaterjs/repeater': 3.0.4 + '@types/ws': 8.5.4 + graphql: 16.6.0 + graphql-ws: 5.12.1(graphql@16.6.0) + isomorphic-ws: 5.0.0(ws@8.13.0) + tslib: 2.5.0 + ws: 8.13.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + + /@graphql-tools/executor-http@0.1.9(@types/node@18.16.0)(graphql@16.6.0): + resolution: {integrity: sha512-tNzMt5qc1ptlHKfpSv9wVBVKCZ7gks6Yb/JcYJluxZIT4qRV+TtOFjpptfBU63usgrGVOVcGjzWc/mt7KhmmpQ==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + '@repeaterjs/repeater': 3.0.4 + '@whatwg-node/fetch': 0.8.8 + dset: 3.1.2 + extract-files: 11.0.0 + graphql: 16.6.0 + meros: 1.2.1(@types/node@18.16.0) + tslib: 2.5.0 + value-or-promise: 1.0.12 + transitivePeerDependencies: + - '@types/node' + dev: true + + /@graphql-tools/executor-legacy-ws@0.0.11(graphql@16.6.0): + resolution: {integrity: sha512-4ai+NnxlNfvIQ4c70hWFvOZlSUN8lt7yc+ZsrwtNFbFPH/EroIzFMapAxM9zwyv9bH38AdO3TQxZ5zNxgBdvUw==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + '@types/ws': 8.5.4 + graphql: 16.6.0 + isomorphic-ws: 5.0.0(ws@8.13.0) + tslib: 2.5.0 + ws: 8.13.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + + /@graphql-tools/executor@0.0.20(graphql@16.6.0): + resolution: {integrity: sha512-GdvNc4vszmfeGvUqlcaH1FjBoguvMYzxAfT6tDd4/LgwymepHhinqLNA5otqwVLW+JETcDaK7xGENzFomuE6TA==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + '@graphql-typed-document-node/core': 3.2.0(graphql@16.6.0) + '@repeaterjs/repeater': 3.0.4 + graphql: 16.6.0 + tslib: 2.5.0 + value-or-promise: 1.0.12 + dev: true + + /@graphql-tools/git-loader@7.2.22(@babel/core@7.21.5)(graphql@16.6.0): + resolution: {integrity: sha512-9rpHggHiOeqA7/ZlKD3c5yXk5bPGw0zkIgKMerjCmFAQAZ6CEVfsa7nAzEWQxn6rpdaBft4/0A56rPMrsUwGBA==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/graphql-tag-pluck': 7.5.2(@babel/core@7.21.5)(graphql@16.6.0) + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + graphql: 16.6.0 + is-glob: 4.0.3 + micromatch: 4.0.5 + tslib: 2.5.0 + unixify: 1.0.0 + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + + /@graphql-tools/github-loader@7.3.28(@babel/core@7.21.5)(@types/node@18.16.0)(graphql@16.6.0): + resolution: {integrity: sha512-OK92Lf9pmxPQvjUNv05b3tnVhw0JRfPqOf15jZjyQ8BfdEUrJoP32b4dRQQem/wyRL24KY4wOfArJNqzpsbwCA==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@ardatan/sync-fetch': 0.0.1 + '@graphql-tools/executor-http': 0.1.9(@types/node@18.16.0)(graphql@16.6.0) + '@graphql-tools/graphql-tag-pluck': 7.5.2(@babel/core@7.21.5)(graphql@16.6.0) + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + '@whatwg-node/fetch': 0.8.8 + graphql: 16.6.0 + tslib: 2.5.0 + value-or-promise: 1.0.12 + transitivePeerDependencies: + - '@babel/core' + - '@types/node' + - encoding + - supports-color + dev: true + + /@graphql-tools/graphql-file-loader@7.5.17(graphql@16.6.0): + resolution: {integrity: sha512-hVwwxPf41zOYgm4gdaZILCYnKB9Zap7Ys9OhY1hbwuAuC4MMNY9GpUjoTU3CQc3zUiPoYStyRtUGkHSJZ3HxBw==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/import': 6.7.18(graphql@16.6.0) + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + globby: 11.1.0 + graphql: 16.6.0 + tslib: 2.5.0 + unixify: 1.0.0 + dev: true + + /@graphql-tools/graphql-tag-pluck@7.5.2(@babel/core@7.21.5)(graphql@16.6.0): + resolution: {integrity: sha512-RW+H8FqOOLQw0BPXaahYepVSRjuOHw+7IL8Opaa5G5uYGOBxoXR7DceyQ7BcpMgktAOOmpDNQ2WtcboChOJSRA==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@babel/parser': 7.21.5 + '@babel/plugin-syntax-import-assertions': 7.20.0(@babel/core@7.21.5) + '@babel/traverse': 7.21.5 + '@babel/types': 7.21.5 + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + graphql: 16.6.0 + tslib: 2.5.0 + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + + /@graphql-tools/import@6.7.18(graphql@16.6.0): + resolution: {integrity: sha512-XQDdyZTp+FYmT7as3xRWH/x8dx0QZA2WZqfMF5EWb36a0PiH7WwlRQYIdyYXj8YCLpiWkeBXgBRHmMnwEYR8iQ==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + graphql: 16.6.0 + resolve-from: 5.0.0 + tslib: 2.5.0 + dev: true + + /@graphql-tools/json-file-loader@7.4.18(graphql@16.6.0): + resolution: {integrity: sha512-AJ1b6Y1wiVgkwsxT5dELXhIVUPs/u3VZ8/0/oOtpcoyO/vAeM5rOvvWegzicOOnQw8G45fgBRMkkRfeuwVt6+w==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + globby: 11.1.0 + graphql: 16.6.0 + tslib: 2.5.0 + unixify: 1.0.0 + dev: true + + /@graphql-tools/load@7.8.14(graphql@16.6.0): + resolution: {integrity: sha512-ASQvP+snHMYm+FhIaLxxFgVdRaM0vrN9wW2BKInQpktwWTXVyk+yP5nQUCEGmn0RTdlPKrffBaigxepkEAJPrg==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/schema': 9.0.19(graphql@16.6.0) + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + graphql: 16.6.0 + p-limit: 3.1.0 + tslib: 2.5.0 + dev: true + + /@graphql-tools/merge@8.4.1(graphql@16.6.0): + resolution: {integrity: sha512-hssnPpZ818mxgl5+GfyOOSnnflAxiaTn1A1AojZcIbh4J52sS1Q0gSuBR5VrnUDjuxiqoCotpXdAQl+K+U6KLQ==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + graphql: 16.6.0 + tslib: 2.5.0 + + /@graphql-tools/optimize@1.4.0(graphql@16.6.0): + resolution: {integrity: sha512-dJs/2XvZp+wgHH8T5J2TqptT9/6uVzIYvA6uFACha+ufvdMBedkfR4b4GbT8jAKLRARiqRTxy3dctnwkTM2tdw==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + graphql: 16.6.0 + tslib: 2.5.0 + dev: true + + /@graphql-tools/prisma-loader@7.2.71(@types/node@18.16.0)(graphql@16.6.0): + resolution: {integrity: sha512-FuIvhRrkduqPdj3QX0/anCxGViEETfoZ/1NvotfM6iVO1XxR75VXvP/iyKGbK6XvYRXwSstgj2DetlQnqdgXhA==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/url-loader': 7.17.18(@types/node@18.16.0)(graphql@16.6.0) + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + '@types/js-yaml': 4.0.5 + '@types/json-stable-stringify': 1.0.34 + '@whatwg-node/fetch': 0.8.8 + chalk: 4.1.2 + debug: 4.3.4 + dotenv: 16.0.3 + graphql: 16.6.0 + graphql-request: 6.0.0(graphql@16.6.0) + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + jose: 4.14.3 + js-yaml: 4.1.0 + json-stable-stringify: 1.0.2 + lodash: 4.17.21 + scuid: 1.1.0 + tslib: 2.5.0 + yaml-ast-parser: 0.0.43 + transitivePeerDependencies: + - '@types/node' + - bufferutil + - encoding + - supports-color + - utf-8-validate + dev: true + + /@graphql-tools/relay-operation-optimizer@6.5.18(graphql@16.6.0): + resolution: {integrity: sha512-mc5VPyTeV+LwiM+DNvoDQfPqwQYhPV/cl5jOBjTgSniyaq8/86aODfMkrE2OduhQ5E00hqrkuL2Fdrgk0w1QJg==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@ardatan/relay-compiler': 12.0.0(graphql@16.6.0) + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + graphql: 16.6.0 + tslib: 2.5.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + + /@graphql-tools/schema@9.0.19(graphql@16.6.0): + resolution: {integrity: sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/merge': 8.4.1(graphql@16.6.0) + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + graphql: 16.6.0 + tslib: 2.5.0 + value-or-promise: 1.0.12 + + /@graphql-tools/url-loader@7.17.18(@types/node@18.16.0)(graphql@16.6.0): + resolution: {integrity: sha512-ear0CiyTj04jCVAxi7TvgbnGDIN2HgqzXzwsfcqiVg9cvjT40NcMlZ2P1lZDgqMkZ9oyLTV8Bw6j+SyG6A+xPw==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@ardatan/sync-fetch': 0.0.1 + '@graphql-tools/delegate': 9.0.35(graphql@16.6.0) + '@graphql-tools/executor-graphql-ws': 0.0.14(graphql@16.6.0) + '@graphql-tools/executor-http': 0.1.9(@types/node@18.16.0)(graphql@16.6.0) + '@graphql-tools/executor-legacy-ws': 0.0.11(graphql@16.6.0) + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + '@graphql-tools/wrap': 9.4.2(graphql@16.6.0) + '@types/ws': 8.5.4 + '@whatwg-node/fetch': 0.8.8 + graphql: 16.6.0 + isomorphic-ws: 5.0.0(ws@8.13.0) + tslib: 2.5.0 + value-or-promise: 1.0.12 + ws: 8.13.0 + transitivePeerDependencies: + - '@types/node' + - bufferutil + - encoding + - utf-8-validate + dev: true + + /@graphql-tools/utils@9.2.1(graphql@16.6.0): + resolution: {integrity: sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.6.0) + graphql: 16.6.0 + tslib: 2.5.0 + + /@graphql-tools/wrap@9.4.2(graphql@16.6.0): + resolution: {integrity: sha512-DFcd9r51lmcEKn0JW43CWkkI2D6T9XI1juW/Yo86i04v43O9w2/k4/nx2XTJv4Yv+iXwUw7Ok81PGltwGJSDSA==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/delegate': 9.0.35(graphql@16.6.0) + '@graphql-tools/schema': 9.0.19(graphql@16.6.0) + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + graphql: 16.6.0 + tslib: 2.5.0 + value-or-promise: 1.0.12 + dev: true + + /@graphql-typed-document-node/core@3.2.0(graphql@16.6.0): + resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + graphql: 16.6.0 + + /@humanwhocodes/config-array@0.11.8: + resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema@1.2.1: + resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + dev: true + + /@istanbuljs/load-nyc-config@1.1.0: + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.1 + resolve-from: 5.0.0 + dev: true + + /@istanbuljs/schema@0.1.3: + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + dev: true + + /@jest/console@29.6.2: + resolution: {integrity: sha512-0N0yZof5hi44HAR2pPS+ikJ3nzKNoZdVu8FffRf3wy47I7Dm7etk/3KetMdRUqzVd16V4O2m2ISpNTbnIuqy1w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.1 + '@types/node': 18.16.0 + chalk: 4.1.2 + jest-message-util: 29.6.2 + jest-util: 29.6.2 + slash: 3.0.0 + dev: true + + /@jest/core@29.6.2(ts-node@10.9.1): + resolution: {integrity: sha512-Oj+5B+sDMiMWLhPFF+4/DvHOf+U10rgvCLGPHP8Xlsy/7QxS51aU/eBngudHlJXnaWD5EohAgJ4js+T6pa+zOg==} + 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 + dependencies: + '@jest/console': 29.6.2 + '@jest/reporters': 29.6.2 + '@jest/test-result': 29.6.2 + '@jest/transform': 29.6.2 + '@jest/types': 29.6.1 + '@types/node': 18.16.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.8.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.5.0 + jest-config: 29.6.2(@types/node@18.16.0)(ts-node@10.9.1) + jest-haste-map: 29.6.2 + jest-message-util: 29.6.2 + jest-regex-util: 29.4.3 + jest-resolve: 29.6.2 + jest-resolve-dependencies: 29.6.2 + jest-runner: 29.6.2 + jest-runtime: 29.6.2 + jest-snapshot: 29.6.2 + jest-util: 29.6.2 + jest-validate: 29.6.2 + jest-watcher: 29.6.2 + micromatch: 4.0.5 + pretty-format: 29.6.2 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /@jest/environment@29.6.2: + resolution: {integrity: sha512-AEcW43C7huGd/vogTddNNTDRpO6vQ2zaQNrttvWV18ArBx9Z56h7BIsXkNFJVOO4/kblWEQz30ckw0+L3izc+Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/fake-timers': 29.6.2 + '@jest/types': 29.6.1 + '@types/node': 18.16.0 + jest-mock: 29.6.2 + dev: true + + /@jest/expect-utils@29.6.2: + resolution: {integrity: sha512-6zIhM8go3RV2IG4aIZaZbxwpOzz3ZiM23oxAlkquOIole+G6TrbeXnykxWYlqF7kz2HlBjdKtca20x9atkEQYg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.4.3 + dev: true + + /@jest/expect@29.6.2: + resolution: {integrity: sha512-m6DrEJxVKjkELTVAztTLyS/7C92Y2b0VYqmDROYKLLALHn8T/04yPs70NADUYPrV3ruI+H3J0iUIuhkjp7vkfg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + expect: 29.6.2 + jest-snapshot: 29.6.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/fake-timers@29.6.2: + resolution: {integrity: sha512-euZDmIlWjm1Z0lJ1D0f7a0/y5Kh/koLFMUBE5SUYWrmy8oNhJpbTBDAP6CxKnadcMLDoDf4waRYCe35cH6G6PA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.1 + '@sinonjs/fake-timers': 10.3.0 + '@types/node': 18.16.0 + jest-message-util: 29.6.2 + jest-mock: 29.6.2 + jest-util: 29.6.2 + dev: true + + /@jest/globals@29.6.2: + resolution: {integrity: sha512-cjuJmNDjs6aMijCmSa1g2TNG4Lby/AeU7/02VtpW+SLcZXzOLK2GpN2nLqcFjmhy3B3AoPeQVx7BnyOf681bAw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.6.2 + '@jest/expect': 29.6.2 + '@jest/types': 29.6.1 + jest-mock: 29.6.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/reporters@29.6.2: + resolution: {integrity: sha512-sWtijrvIav8LgfJZlrGCdN0nP2EWbakglJY49J1Y5QihcQLfy7ovyxxjJBRXMNltgt4uPtEcFmIMbVshEDfFWw==} + 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 + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.6.2 + '@jest/test-result': 29.6.2 + '@jest/transform': 29.6.2 + '@jest/types': 29.6.1 + '@jridgewell/trace-mapping': 0.3.18 + '@types/node': 18.16.0 + chalk: 4.1.2 + collect-v8-coverage: 1.0.2 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.0 + istanbul-lib-instrument: 5.2.1 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.6 + jest-message-util: 29.6.2 + jest-util: 29.6.2 + jest-worker: 29.6.2 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.1.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/schemas@29.6.0: + resolution: {integrity: sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true + + /@jest/source-map@29.6.0: + resolution: {integrity: sha512-oA+I2SHHQGxDCZpbrsCQSoMLb3Bz547JnM+jUr9qEbuw0vQlWZfpPS7CO9J7XiwKicEz9OFn/IYoLkkiUD7bzA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jridgewell/trace-mapping': 0.3.18 + callsites: 3.1.0 + graceful-fs: 4.2.11 + dev: true + + /@jest/test-result@29.6.2: + resolution: {integrity: sha512-3VKFXzcV42EYhMCsJQURptSqnyjqCGbtLuX5Xxb6Pm6gUf1wIRIl+mandIRGJyWKgNKYF9cnstti6Ls5ekduqw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.6.2 + '@jest/types': 29.6.1 + '@types/istanbul-lib-coverage': 2.0.4 + collect-v8-coverage: 1.0.2 + dev: true + + /@jest/test-sequencer@29.6.2: + resolution: {integrity: sha512-GVYi6PfPwVejO7slw6IDO0qKVum5jtrJ3KoLGbgBWyr2qr4GaxFV6su+ZAjdTX75Sr1DkMFRk09r2ZVa+wtCGw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.6.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.6.2 + slash: 3.0.0 + dev: true + + /@jest/transform@29.6.2: + resolution: {integrity: sha512-ZqCqEISr58Ce3U+buNFJYUktLJZOggfyvR+bZMaiV1e8B1SIvJbwZMrYz3gx/KAPn9EXmOmN+uB08yLCjWkQQg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.21.5 + '@jest/types': 29.6.1 + '@jridgewell/trace-mapping': 0.3.18 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.6.2 + jest-regex-util: 29.4.3 + jest-util: 29.6.2 + micromatch: 4.0.5 + pirates: 4.0.6 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/types@29.6.1: + resolution: {integrity: sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.0 + '@types/istanbul-lib-coverage': 2.0.4 + '@types/istanbul-reports': 3.0.1 + '@types/node': 18.16.0 + '@types/yargs': 17.0.24 + chalk: 4.1.2 + dev: true + + /@josephg/resolvable@1.0.1: + resolution: {integrity: sha512-CtzORUwWTTOTqfVtHaKRJ0I1kNQd1bpn3sUh8I3nJDVY+5/M/Oe1DnEWzPQvqq/xPIIkzzzIP7mfCoAjFRvDhg==} + dev: false + + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.18 + dev: true + + /@jridgewell/resolve-uri@3.1.0: + resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/set-array@1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/sourcemap-codec@1.4.14: + resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.18: + resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==} + dependencies: + '@jridgewell/resolve-uri': 3.1.0 + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + + /@jridgewell/trace-mapping@0.3.9: + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.0 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + + /@parcel/watcher@2.1.0: + resolution: {integrity: sha512-8s8yYjd19pDSsBpbkOHnT6Z2+UJSuLQx61pCFM0s5wSRvKCEMDjd/cHY3/GI1szHIWbpXpsJdg3V6ISGGx9xDw==} + engines: {node: '>= 10.0.0'} + requiresBuild: true + dependencies: + is-glob: 4.0.3 + micromatch: 4.0.5 + node-addon-api: 3.2.1 + node-gyp-build: 4.6.0 + dev: true + + /@peculiar/asn1-schema@2.3.6: + resolution: {integrity: sha512-izNRxPoaeJeg/AyH8hER6s+H7p4itk+03QCa4sbxI3lNdseQYCuxzgsuNK8bTXChtLTjpJz6NmXKA73qLa3rCA==} + dependencies: + asn1js: 3.0.5 + pvtsutils: 1.3.2 + tslib: 2.5.0 + dev: true + + /@peculiar/json-schema@1.1.12: + resolution: {integrity: sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==} + engines: {node: '>=8.0.0'} + dependencies: + tslib: 2.5.0 + dev: true + + /@peculiar/webcrypto@1.4.3: + resolution: {integrity: sha512-VtaY4spKTdN5LjJ04im/d/joXuvLbQdgy5Z4DXF4MFZhQ+MTrejbNMkfZBp1Bs3O5+bFqnJgyGdPuZQflvIa5A==} + engines: {node: '>=10.12.0'} + dependencies: + '@peculiar/asn1-schema': 2.3.6 + '@peculiar/json-schema': 1.1.12 + pvtsutils: 1.3.2 + tslib: 2.5.0 + webcrypto-core: 1.7.7 + dev: true + + /@protobufjs/aspromise@1.1.2: + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + dev: false + + /@protobufjs/base64@1.1.2: + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + dev: false + + /@protobufjs/codegen@2.0.4: + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + dev: false + + /@protobufjs/eventemitter@1.1.0: + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + dev: false + + /@protobufjs/fetch@1.1.0: + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + dev: false + + /@protobufjs/float@1.0.2: + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + dev: false + + /@protobufjs/inquire@1.1.0: + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + dev: false + + /@protobufjs/path@1.1.2: + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + dev: false + + /@protobufjs/pool@1.1.0: + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + dev: false + + /@protobufjs/utf8@1.1.0: + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + dev: false + + /@repeaterjs/repeater@3.0.4: + resolution: {integrity: sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA==} + dev: true + + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true + + /@sinonjs/commons@3.0.0: + resolution: {integrity: sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==} + dependencies: + type-detect: 4.0.8 + dev: true + + /@sinonjs/fake-timers@10.3.0: + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + dependencies: + '@sinonjs/commons': 3.0.0 + dev: true + + /@tootallnate/once@2.0.0: + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + + /@tsconfig/node10@1.0.9: + resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} + dev: true + + /@tsconfig/node12@1.0.11: + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: true + + /@tsconfig/node14@1.0.3: + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + dev: true + + /@tsconfig/node16@1.0.4: + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + dev: true + + /@types/babel__core@7.20.1: + resolution: {integrity: sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==} + dependencies: + '@babel/parser': 7.21.5 + '@babel/types': 7.21.5 + '@types/babel__generator': 7.6.4 + '@types/babel__template': 7.4.1 + '@types/babel__traverse': 7.20.1 + dev: true + + /@types/babel__generator@7.6.4: + resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} + dependencies: + '@babel/types': 7.21.5 + dev: true + + /@types/babel__template@7.4.1: + resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} + dependencies: + '@babel/parser': 7.21.5 + '@babel/types': 7.21.5 + dev: true + + /@types/babel__traverse@7.20.1: + resolution: {integrity: sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==} + dependencies: + '@babel/types': 7.21.5 + dev: true + + /@types/body-parser@1.19.2: + resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} + dependencies: + '@types/connect': 3.4.35 + '@types/node': 18.16.0 + dev: false + + /@types/connect@3.4.35: + resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} + dependencies: + '@types/node': 18.16.0 + dev: false + + /@types/express-serve-static-core@4.17.33: + resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==} + dependencies: + '@types/node': 18.16.0 + '@types/qs': 6.9.7 + '@types/range-parser': 1.2.4 + dev: false + + /@types/express@4.17.17: + resolution: {integrity: sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==} + dependencies: + '@types/body-parser': 1.19.2 + '@types/express-serve-static-core': 4.17.33 + '@types/qs': 6.9.7 + '@types/serve-static': 1.15.1 + dev: false + + /@types/graceful-fs@4.1.6: + resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==} + dependencies: + '@types/node': 18.16.0 + dev: true + + /@types/istanbul-lib-coverage@2.0.4: + resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} + dev: true + + /@types/istanbul-lib-report@3.0.0: + resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} + dependencies: + '@types/istanbul-lib-coverage': 2.0.4 + dev: true + + /@types/istanbul-reports@3.0.1: + resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} + dependencies: + '@types/istanbul-lib-report': 3.0.0 + dev: true + + /@types/js-yaml@4.0.5: + resolution: {integrity: sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==} + dev: true + + /@types/jsdom@21.1.1: + resolution: {integrity: sha512-cZFuoVLtzKP3gmq9eNosUL1R50U+USkbLtUQ1bYVgl/lKp0FZM7Cq4aIHAL8oIvQ17uSHi7jXPtfDOdjPwBE7A==} + dependencies: + '@types/node': 18.16.0 + '@types/tough-cookie': 4.0.2 + parse5: 7.1.2 + dev: true + + /@types/json-schema@7.0.11: + resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} + dev: true + + /@types/json-stable-stringify@1.0.34: + resolution: {integrity: sha512-s2cfwagOQAS8o06TcwKfr9Wx11dNGbH2E9vJz1cqV+a/LOyhWNLUNd6JSRYNzvB4d29UuJX2M0Dj9vE1T8fRXw==} + dev: true + + /@types/json5@0.0.29: + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + dev: true + + /@types/long@4.0.2: + resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} + dev: false + + /@types/luxon@3.3.1: + resolution: {integrity: sha512-XOS5nBcgEeP2PpcqJHjCWhUCAzGfXIU8ILOSLpx2FhxqMW9KdxgCGXNOEKGVBfveKtIpztHzKK5vSRVLyW/NqA==} + dev: true + + /@types/mime@3.0.1: + resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==} + dev: false + + /@types/node-fetch@2.6.3: + resolution: {integrity: sha512-ETTL1mOEdq/sxUtgtOhKjyB2Irra4cjxksvcMUR5Zr4n+PxVhsCD9WS46oPbHL3et9Zde7CNRr+WUNlcHvsX+w==} + dependencies: + '@types/node': 18.16.0 + form-data: 3.0.1 + dev: false + + /@types/node@18.16.0: + resolution: {integrity: sha512-BsAaKhB+7X+H4GnSjGhJG9Qi8Tw+inU9nJDwmD5CgOmBLEI6ArdhikpLX7DjbjDRDTbqZzU2LSQNZg8WGPiSZQ==} + + /@types/parse-json@4.0.0: + resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} + dev: true + + /@types/qs@6.9.7: + resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==} + dev: false + + /@types/range-parser@1.2.4: + resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} + dev: false + + /@types/semver@7.3.13: + resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==} + dev: true + + /@types/serve-static@1.15.1: + resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==} + dependencies: + '@types/mime': 3.0.1 + '@types/node': 18.16.0 + dev: false + + /@types/stack-utils@2.0.1: + resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} + dev: true + + /@types/tough-cookie@4.0.2: + resolution: {integrity: sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==} + dev: true + + /@types/triple-beam@1.3.2: + resolution: {integrity: sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==} + dev: false + + /@types/ws@8.5.4: + resolution: {integrity: sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==} + dependencies: + '@types/node': 18.16.0 + dev: true + + /@types/yargs-parser@21.0.0: + resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} + dev: true + + /@types/yargs@17.0.24: + resolution: {integrity: sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==} + dependencies: + '@types/yargs-parser': 21.0.0 + dev: true + + /@typescript-eslint/eslint-plugin@5.59.0(@typescript-eslint/parser@5.59.0)(eslint@8.39.0)(typescript@5.0.4): + resolution: {integrity: sha512-p0QgrEyrxAWBecR56gyn3wkG15TJdI//eetInP3zYRewDh0XS+DhB3VUAd3QqvziFsfaQIoIuZMxZRB7vXYaYw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.5.0 + '@typescript-eslint/parser': 5.59.0(eslint@8.39.0)(typescript@5.0.4) + '@typescript-eslint/scope-manager': 5.59.0 + '@typescript-eslint/type-utils': 5.59.0(eslint@8.39.0)(typescript@5.0.4) + '@typescript-eslint/utils': 5.59.0(eslint@8.39.0)(typescript@5.0.4) + debug: 4.3.4 + eslint: 8.39.0 + grapheme-splitter: 1.0.4 + ignore: 5.2.4 + natural-compare-lite: 1.4.0 + semver: 7.5.0 + tsutils: 3.21.0(typescript@5.0.4) + typescript: 5.0.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@5.59.0(eslint@8.39.0)(typescript@5.0.4): + resolution: {integrity: sha512-qK9TZ70eJtjojSUMrrEwA9ZDQ4N0e/AuoOIgXuNBorXYcBDk397D2r5MIe1B3cok/oCtdNC5j+lUUpVB+Dpb+w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.59.0 + '@typescript-eslint/types': 5.59.0 + '@typescript-eslint/typescript-estree': 5.59.0(typescript@5.0.4) + debug: 4.3.4 + eslint: 8.39.0 + typescript: 5.0.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@5.59.0: + resolution: {integrity: sha512-tsoldKaMh7izN6BvkK6zRMINj4Z2d6gGhO2UsI8zGZY3XhLq1DndP3Ycjhi1JwdwPRwtLMW4EFPgpuKhbCGOvQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.59.0 + '@typescript-eslint/visitor-keys': 5.59.0 + dev: true + + /@typescript-eslint/type-utils@5.59.0(eslint@8.39.0)(typescript@5.0.4): + resolution: {integrity: sha512-d/B6VSWnZwu70kcKQSCqjcXpVH+7ABKH8P1KNn4K7j5PXXuycZTPXF44Nui0TEm6rbWGi8kc78xRgOC4n7xFgA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 5.59.0(typescript@5.0.4) + '@typescript-eslint/utils': 5.59.0(eslint@8.39.0)(typescript@5.0.4) + debug: 4.3.4 + eslint: 8.39.0 + tsutils: 3.21.0(typescript@5.0.4) + typescript: 5.0.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@5.59.0: + resolution: {integrity: sha512-yR2h1NotF23xFFYKHZs17QJnB51J/s+ud4PYU4MqdZbzeNxpgUr05+dNeCN/bb6raslHvGdd6BFCkVhpPk/ZeA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@typescript-eslint/typescript-estree@5.59.0(typescript@5.0.4): + resolution: {integrity: sha512-sUNnktjmI8DyGzPdZ8dRwW741zopGxltGs/SAPgGL/AAgDpiLsCFLcMNSpbfXfmnNeHmK9h3wGmCkGRGAoUZAg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.59.0 + '@typescript-eslint/visitor-keys': 5.59.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.4 + tsutils: 3.21.0(typescript@5.0.4) + typescript: 5.0.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@5.59.0(eslint@8.39.0)(typescript@5.0.4): + resolution: {integrity: sha512-GGLFd+86drlHSvPgN/el6dRQNYYGOvRSDVydsUaQluwIW3HvbXuxyuD5JETvBt/9qGYe+lOrDk6gRrWOHb/FvA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.39.0) + '@types/json-schema': 7.0.11 + '@types/semver': 7.3.13 + '@typescript-eslint/scope-manager': 5.59.0 + '@typescript-eslint/types': 5.59.0 + '@typescript-eslint/typescript-estree': 5.59.0(typescript@5.0.4) + eslint: 8.39.0 + eslint-scope: 5.1.1 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@5.59.0: + resolution: {integrity: sha512-qZ3iXxQhanchCeaExlKPV3gDQFxMUmU35xfd5eCXB6+kUw1TUAbIy2n7QIrwz9s98DQLzNWyHp61fY0da4ZcbA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.59.0 + eslint-visitor-keys: 3.4.0 + dev: true + + /@whatwg-node/events@0.0.3: + resolution: {integrity: sha512-IqnKIDWfXBJkvy/k6tzskWTc2NK3LcqHlb+KHGCrjOCH4jfQckRX0NAiIcC/vIqQkzLYw2r2CTSwAxcrtcD6lA==} + dev: true + + /@whatwg-node/fetch@0.8.8: + resolution: {integrity: sha512-CdcjGC2vdKhc13KKxgsc6/616BQ7ooDIgPeTuAiE8qfCnS0mGzcfCOoZXypQSz73nxI+GWc7ZReIAVhxoE1KCg==} + dependencies: + '@peculiar/webcrypto': 1.4.3 + '@whatwg-node/node-fetch': 0.3.6 + busboy: 1.6.0 + urlpattern-polyfill: 8.0.2 + web-streams-polyfill: 3.2.1 + dev: true + + /@whatwg-node/node-fetch@0.3.6: + resolution: {integrity: sha512-w9wKgDO4C95qnXZRwZTfCmLWqyRnooGjcIwG0wADWjw9/HN0p7dtvtgSvItZtUyNteEvgTrd8QojNEqV6DAGTA==} + dependencies: + '@whatwg-node/events': 0.0.3 + busboy: 1.6.0 + fast-querystring: 1.1.1 + fast-url-parser: 1.1.3 + tslib: 2.5.0 + dev: true + + /abab@2.0.6: + resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} + dev: false + + /accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + dev: false + + /acorn-globals@7.0.1: + resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} + dependencies: + acorn: 8.8.2 + acorn-walk: 8.2.0 + dev: false + + /acorn-jsx@5.3.2(acorn@8.8.2): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.8.2 + dev: true + + /acorn-walk@8.2.0: + resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} + engines: {node: '>=0.4.0'} + + /acorn@8.8.2: + resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} + engines: {node: '>=0.4.0'} + hasBin: true + + /agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + dependencies: + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + + /aggregate-error@3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + dependencies: + clean-stack: 2.2.0 + indent-string: 4.0.0 + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.21.3 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + + /ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: true + + /argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + dependencies: + sprintf-js: 1.0.3 + dev: true + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /array-buffer-byte-length@1.0.0: + resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} + dependencies: + call-bind: 1.0.2 + is-array-buffer: 3.0.2 + dev: true + + /array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + dev: false + + /array-includes@3.1.6: + resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + get-intrinsic: 1.2.0 + is-string: 1.0.7 + dev: true + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /array.prototype.flat@1.3.1: + resolution: {integrity: sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + es-shim-unscopables: 1.0.0 + dev: true + + /array.prototype.flatmap@1.3.1: + resolution: {integrity: sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + es-shim-unscopables: 1.0.0 + dev: true + + /asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + dev: true + + /asn1js@3.0.5: + resolution: {integrity: sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==} + engines: {node: '>=12.0.0'} + dependencies: + pvtsutils: 1.3.2 + pvutils: 1.1.3 + tslib: 2.5.0 + dev: true + + /astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + dev: true + + /async-retry@1.3.3: + resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} + dependencies: + retry: 0.13.1 + dev: false + + /async@3.2.4: + resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} + dev: false + + /asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: false + + /auto-bind@4.0.0: + resolution: {integrity: sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==} + engines: {node: '>=8'} + dev: true + + /available-typed-arrays@1.0.5: + resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} + engines: {node: '>= 0.4'} + dev: true + + /babel-jest@29.6.2(@babel/core@7.21.5): + resolution: {integrity: sha512-BYCzImLos6J3BH/+HvUCHG1dTf2MzmAB4jaVxHV+29RZLjR29XuYTmsf2sdDwkrb+FczkGo3kOhE7ga6sI0P4A==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + dependencies: + '@babel/core': 7.21.5 + '@jest/transform': 29.6.2 + '@types/babel__core': 7.20.1 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.5.0(@babel/core@7.21.5) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-istanbul@6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} + dependencies: + '@babel/helper-plugin-utils': 7.21.5 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-jest-hoist@29.5.0: + resolution: {integrity: sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/template': 7.20.7 + '@babel/types': 7.21.5 + '@types/babel__core': 7.20.1 + '@types/babel__traverse': 7.20.1 + dev: true + + /babel-plugin-syntax-trailing-function-commas@7.0.0-beta.0: + resolution: {integrity: sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ==} + dev: true + + /babel-preset-current-node-syntax@1.0.1(@babel/core@7.21.5): + resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.21.5 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.21.5) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.21.5) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.21.5) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.21.5) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.21.5) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.21.5) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.21.5) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.21.5) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.5) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.5) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.5) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.21.5) + dev: true + + /babel-preset-fbjs@3.4.0(@babel/core@7.21.5): + resolution: {integrity: sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow==} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.21.5 + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.21.5) + '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.21.5) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.21.5) + '@babel/plugin-syntax-flow': 7.21.4(@babel/core@7.21.5) + '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.21.5) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.5) + '@babel/plugin-transform-arrow-functions': 7.21.5(@babel/core@7.21.5) + '@babel/plugin-transform-block-scoped-functions': 7.18.6(@babel/core@7.21.5) + '@babel/plugin-transform-block-scoping': 7.21.0(@babel/core@7.21.5) + '@babel/plugin-transform-classes': 7.21.0(@babel/core@7.21.5) + '@babel/plugin-transform-computed-properties': 7.21.5(@babel/core@7.21.5) + '@babel/plugin-transform-destructuring': 7.21.3(@babel/core@7.21.5) + '@babel/plugin-transform-flow-strip-types': 7.21.0(@babel/core@7.21.5) + '@babel/plugin-transform-for-of': 7.21.5(@babel/core@7.21.5) + '@babel/plugin-transform-function-name': 7.18.9(@babel/core@7.21.5) + '@babel/plugin-transform-literals': 7.18.9(@babel/core@7.21.5) + '@babel/plugin-transform-member-expression-literals': 7.18.6(@babel/core@7.21.5) + '@babel/plugin-transform-modules-commonjs': 7.21.5(@babel/core@7.21.5) + '@babel/plugin-transform-object-super': 7.18.6(@babel/core@7.21.5) + '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.21.5) + '@babel/plugin-transform-property-literals': 7.18.6(@babel/core@7.21.5) + '@babel/plugin-transform-react-display-name': 7.18.6(@babel/core@7.21.5) + '@babel/plugin-transform-react-jsx': 7.21.5(@babel/core@7.21.5) + '@babel/plugin-transform-shorthand-properties': 7.18.6(@babel/core@7.21.5) + '@babel/plugin-transform-spread': 7.20.7(@babel/core@7.21.5) + '@babel/plugin-transform-template-literals': 7.18.9(@babel/core@7.21.5) + babel-plugin-syntax-trailing-function-commas: 7.0.0-beta.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-preset-jest@29.5.0(@babel/core@7.21.5): + resolution: {integrity: sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.21.5 + babel-plugin-jest-hoist: 29.5.0 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.21.5) + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + dev: true + + /bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: true + + /body-parser@1.20.1: + resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + 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.1 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: false + + /body-parser@1.20.2: + resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + 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 + transitivePeerDependencies: + - supports-color + dev: false + + /boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + dev: false + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /browserslist@4.21.5: + resolution: {integrity: sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001481 + electron-to-chromium: 1.4.377 + node-releases: 2.0.10 + update-browserslist-db: 1.0.11(browserslist@4.21.5) + dev: true + + /bs-logger@0.2.6: + resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} + engines: {node: '>= 6'} + dependencies: + fast-json-stable-stringify: 2.1.0 + dev: true + + /bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + dependencies: + node-int64: 0.4.0 + dev: true + + /buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: true + + /buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: true + + /builtins@5.0.1: + resolution: {integrity: sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==} + dependencies: + semver: 7.5.4 + dev: true + + /busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + dependencies: + streamsearch: 1.1.0 + dev: true + + /bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + dev: false + + /call-bind@1.0.2: + resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + dependencies: + function-bind: 1.1.1 + get-intrinsic: 1.2.0 + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /camel-case@4.1.2: + resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} + dependencies: + pascal-case: 3.1.2 + tslib: 2.5.0 + dev: true + + /camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + dev: true + + /camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + dev: true + + /caniuse-lite@1.0.30001481: + resolution: {integrity: sha512-KCqHwRnaa1InZBtqXzP98LPg0ajCVujMKjqKDhZEthIpAsJl/YEIa3YvXjGXPVqzZVguccuu7ga9KOE1J9rKPQ==} + dev: true + + /capital-case@1.0.4: + resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} + dependencies: + no-case: 3.0.4 + tslib: 2.5.0 + upper-case-first: 2.0.2 + dev: true + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /change-case-all@1.0.15: + resolution: {integrity: sha512-3+GIFhk3sNuvFAJKU46o26OdzudQlPNBCu1ZQi3cMeMHhty1bhDxu2WrEilVNYaGvqUtR1VSigFcJOiS13dRhQ==} + dependencies: + change-case: 4.1.2 + is-lower-case: 2.0.2 + is-upper-case: 2.0.2 + lower-case: 2.0.2 + lower-case-first: 2.0.2 + sponge-case: 1.0.1 + swap-case: 2.0.2 + title-case: 3.0.3 + upper-case: 2.0.2 + upper-case-first: 2.0.2 + dev: true + + /change-case@4.1.2: + resolution: {integrity: sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==} + dependencies: + camel-case: 4.1.2 + capital-case: 1.0.4 + constant-case: 3.0.4 + dot-case: 3.0.4 + header-case: 2.0.4 + no-case: 3.0.4 + param-case: 3.0.4 + pascal-case: 3.1.2 + path-case: 3.0.4 + sentence-case: 3.0.4 + snake-case: 3.0.4 + tslib: 2.5.0 + dev: true + + /char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + dev: true + + /chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + dev: true + + /cheerio-select@2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} + dependencies: + boolbase: 1.0.0 + css-select: 5.1.0 + css-what: 6.1.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.0.1 + dev: false + + /cheerio@1.0.0-rc.12: + resolution: {integrity: sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==} + engines: {node: '>= 6'} + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.0.1 + htmlparser2: 8.0.2 + parse5: 7.1.2 + parse5-htmlparser2-tree-adapter: 7.0.0 + dev: false + + /ci-info@3.8.0: + resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} + engines: {node: '>=8'} + dev: true + + /cjs-module-lexer@1.2.3: + resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} + dev: true + + /clean-stack@2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + dev: true + + /cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + dependencies: + restore-cursor: 3.1.0 + dev: true + + /cli-spinners@2.8.0: + resolution: {integrity: sha512-/eG5sJcvEIwxcdYM86k5tPwn0MUzkX5YY3eImTGpJOZgVe4SdTMY14vQpcxgBzJ0wXwAYrS8E+c3uHeK4JNyzQ==} + engines: {node: '>=6'} + dev: true + + /cli-truncate@2.1.0: + resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} + engines: {node: '>=8'} + dependencies: + slice-ansi: 3.0.0 + string-width: 4.2.3 + dev: true + + /cli-width@3.0.0: + resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} + engines: {node: '>= 10'} + dev: true + + /cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + dev: true + + /cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + /clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + dev: true + + /co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + dev: true + + /collect-v8-coverage@1.0.2: + resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} + dev: true + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + /color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + dev: false + + /color@3.2.1: + resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + dependencies: + color-convert: 1.9.3 + color-string: 1.9.1 + dev: false + + /colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + dev: true + + /colorspace@1.1.4: + resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} + dependencies: + color: 3.2.1 + text-hex: 1.0.0 + dev: false + + /combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + dev: false + + /common-tags@1.8.2: + resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} + engines: {node: '>=4.0.0'} + dev: true + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /constant-case@3.0.4: + resolution: {integrity: sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==} + dependencies: + no-case: 3.0.4 + tslib: 2.5.0 + upper-case: 2.0.2 + dev: true + + /content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + dependencies: + safe-buffer: 5.2.1 + dev: false + + /content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + dev: false + + /convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + dev: true + + /convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + dev: true + + /cookie-signature@1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + dev: false + + /cookie@0.5.0: + resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + engines: {node: '>= 0.6'} + dev: false + + /cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + dev: false + + /cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + dependencies: + '@types/parse-json': 4.0.0 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + dev: true + + /cosmiconfig@8.0.0: + resolution: {integrity: sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==} + engines: {node: '>=14'} + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + dev: true + + /create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: true + + /cross-fetch@3.1.5: + resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==} + dependencies: + node-fetch: 2.6.7 + transitivePeerDependencies: + - encoding + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /css-select@5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.0.1 + nth-check: 2.1.1 + dev: false + + /css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + dev: false + + /cssstyle@3.0.0: + resolution: {integrity: sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==} + engines: {node: '>=14'} + dependencies: + rrweb-cssom: 0.6.0 + dev: false + + /data-urls@4.0.0: + resolution: {integrity: sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g==} + engines: {node: '>=14'} + dependencies: + abab: 2.0.6 + whatwg-mimetype: 3.0.0 + whatwg-url: 12.0.1 + dev: false + + /dataloader@2.2.2: + resolution: {integrity: sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==} + dev: true + + /dateformat@3.0.3: + resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==} + dev: true + + /debounce@1.2.1: + resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==} + dev: true + + /debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + dev: false + + /debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + dev: true + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + + /decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + dev: true + + /decimal.js@10.4.3: + resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} + dev: false + + /dedent@1.5.1: + resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + dev: true + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + /deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + dev: true + + /defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + dependencies: + clone: 1.0.4 + dev: true + + /define-properties@1.2.0: + resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} + engines: {node: '>= 0.4'} + dependencies: + has-property-descriptors: 1.0.0 + object-keys: 1.1.1 + dev: true + + /delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + dev: false + + /depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + dev: false + + /dependency-graph@0.11.0: + resolution: {integrity: sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==} + engines: {node: '>= 0.6.0'} + dev: true + + /destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dev: false + + /detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + dev: true + + /detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + dev: true + + /diff-sequences@29.4.3: + resolution: {integrity: sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + dev: true + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + dev: false + + /domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + dev: false + + /domexception@4.0.0: + resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==} + engines: {node: '>=12'} + dependencies: + webidl-conversions: 7.0.0 + dev: false + + /domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + dependencies: + domelementtype: 2.3.0 + dev: false + + /domutils@3.0.1: + resolution: {integrity: sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==} + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + dev: false + + /dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + dependencies: + no-case: 3.0.4 + tslib: 2.5.0 + dev: true + + /dotenv@16.0.3: + resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} + engines: {node: '>=12'} + dev: true + + /dset@3.1.2: + resolution: {integrity: sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q==} + engines: {node: '>=4'} + dev: true + + /dynamic-dedupe@0.3.0: + resolution: {integrity: sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==} + dependencies: + xtend: 4.0.2 + dev: true + + /ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + dev: false + + /electron-to-chromium@1.4.377: + resolution: {integrity: sha512-H3BYG6DW5Z+l0xcfXaicJGxrpA4kMlCxnN71+iNX+dBLkRMOdVJqFJiAmbNZZKA1zISpRg17JR03qGifXNsJtw==} + dev: true + + /emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + dev: true + + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + /enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + dev: false + + /encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + dev: false + + /entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + /error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 + dev: true + + /es-abstract@1.21.2: + resolution: {integrity: sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + es-set-tostringtag: 2.0.1 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.5 + get-intrinsic: 1.2.0 + get-symbol-description: 1.0.0 + globalthis: 1.0.3 + gopd: 1.0.1 + has: 1.0.3 + has-property-descriptors: 1.0.0 + has-proto: 1.0.1 + has-symbols: 1.0.3 + internal-slot: 1.0.5 + is-array-buffer: 3.0.2 + is-callable: 1.2.7 + is-negative-zero: 2.0.2 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.2 + is-string: 1.0.7 + is-typed-array: 1.1.10 + is-weakref: 1.0.2 + object-inspect: 1.12.3 + object-keys: 1.1.1 + object.assign: 4.1.4 + regexp.prototype.flags: 1.5.0 + safe-regex-test: 1.0.0 + string.prototype.trim: 1.2.7 + string.prototype.trimend: 1.0.6 + string.prototype.trimstart: 1.0.6 + typed-array-length: 1.0.4 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.9 + dev: true + + /es-set-tostringtag@2.0.1: + resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.0 + has: 1.0.3 + has-tostringtag: 1.0.0 + dev: true + + /es-shim-unscopables@1.0.0: + resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} + dependencies: + has: 1.0.3 + dev: true + + /es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: true + + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + + /escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + dev: false + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + + /escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + dev: true + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /escodegen@2.0.0: + resolution: {integrity: sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==} + engines: {node: '>=6.0'} + hasBin: true + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionator: 0.8.3 + optionalDependencies: + source-map: 0.6.1 + dev: false + + /eslint-config-prettier@8.8.0(eslint@8.39.0): + resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 8.39.0 + dev: true + + /eslint-config-standard-with-typescript@34.0.1(@typescript-eslint/eslint-plugin@5.59.0)(eslint-plugin-import@2.27.5)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.39.0)(typescript@5.0.4): + resolution: {integrity: sha512-J7WvZeLtd0Vr9F+v4dZbqJCLD16cbIy4U+alJMq4MiXdpipdBM3U5NkXaGUjePc4sb1ZE01U9g6VuTBpHHz1fg==} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^5.43.0 + eslint: ^8.0.1 + eslint-plugin-import: ^2.25.2 + eslint-plugin-n: ^15.0.0 + eslint-plugin-promise: ^6.0.0 + typescript: '*' + dependencies: + '@typescript-eslint/eslint-plugin': 5.59.0(@typescript-eslint/parser@5.59.0)(eslint@8.39.0)(typescript@5.0.4) + '@typescript-eslint/parser': 5.59.0(eslint@8.39.0)(typescript@5.0.4) + eslint: 8.39.0 + eslint-config-standard: 17.0.0(eslint-plugin-import@2.27.5)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.39.0) + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.59.0)(eslint@8.39.0) + eslint-plugin-n: 15.7.0(eslint@8.39.0) + eslint-plugin-promise: 6.1.1(eslint@8.39.0) + typescript: 5.0.4 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-config-standard@17.0.0(eslint-plugin-import@2.27.5)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.39.0): + resolution: {integrity: sha512-/2ks1GKyqSOkH7JFvXJicu0iMpoojkwB+f5Du/1SC0PtBL+s8v30k9njRZ21pm2drKYm2342jFnGWzttxPmZVg==} + peerDependencies: + eslint: ^8.0.1 + eslint-plugin-import: ^2.25.2 + eslint-plugin-n: ^15.0.0 + eslint-plugin-promise: ^6.0.0 + dependencies: + eslint: 8.39.0 + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.59.0)(eslint@8.39.0) + eslint-plugin-n: 15.7.0(eslint@8.39.0) + eslint-plugin-promise: 6.1.1(eslint@8.39.0) + dev: true + + /eslint-import-resolver-node@0.3.7: + resolution: {integrity: sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==} + dependencies: + debug: 3.2.7 + is-core-module: 2.12.0 + resolve: 1.22.2 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.59.0)(eslint-import-resolver-node@0.3.7)(eslint@8.39.0): + resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + dependencies: + '@typescript-eslint/parser': 5.59.0(eslint@8.39.0)(typescript@5.0.4) + debug: 3.2.7 + eslint: 8.39.0 + eslint-import-resolver-node: 0.3.7 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-es@4.1.0(eslint@8.39.0): + resolution: {integrity: sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==} + engines: {node: '>=8.10.0'} + peerDependencies: + eslint: '>=4.19.1' + dependencies: + eslint: 8.39.0 + eslint-utils: 2.1.0 + regexpp: 3.2.0 + dev: true + + /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.59.0)(eslint@8.39.0): + resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + dependencies: + '@typescript-eslint/parser': 5.59.0(eslint@8.39.0)(typescript@5.0.4) + array-includes: 3.1.6 + array.prototype.flat: 1.3.1 + array.prototype.flatmap: 1.3.1 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 8.39.0 + eslint-import-resolver-node: 0.3.7 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.59.0)(eslint-import-resolver-node@0.3.7)(eslint@8.39.0) + has: 1.0.3 + is-core-module: 2.12.0 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.values: 1.1.6 + resolve: 1.22.2 + semver: 6.3.0 + tsconfig-paths: 3.14.2 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: true + + /eslint-plugin-n@15.7.0(eslint@8.39.0): + resolution: {integrity: sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q==} + engines: {node: '>=12.22.0'} + peerDependencies: + eslint: '>=7.0.0' + dependencies: + builtins: 5.0.1 + eslint: 8.39.0 + eslint-plugin-es: 4.1.0(eslint@8.39.0) + eslint-utils: 3.0.0(eslint@8.39.0) + ignore: 5.2.4 + is-core-module: 2.12.0 + minimatch: 3.1.2 + resolve: 1.22.2 + semver: 7.5.0 + dev: true + + /eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.8.0)(eslint@8.39.0)(prettier@2.8.8): + resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==} + engines: {node: '>=12.0.0'} + peerDependencies: + eslint: '>=7.28.0' + eslint-config-prettier: '*' + prettier: '>=2.0.0' + peerDependenciesMeta: + eslint-config-prettier: + optional: true + dependencies: + eslint: 8.39.0 + eslint-config-prettier: 8.8.0(eslint@8.39.0) + prettier: 2.8.8 + prettier-linter-helpers: 1.0.0 + dev: true + + /eslint-plugin-promise@6.1.1(eslint@8.39.0): + resolution: {integrity: sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + eslint: 8.39.0 + dev: true + + /eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + dev: true + + /eslint-scope@7.2.0: + resolution: {integrity: sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-utils@2.1.0: + resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==} + engines: {node: '>=6'} + dependencies: + eslint-visitor-keys: 1.3.0 + dev: true + + /eslint-utils@3.0.0(eslint@8.39.0): + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + dependencies: + eslint: 8.39.0 + eslint-visitor-keys: 2.1.0 + dev: true + + /eslint-visitor-keys@1.3.0: + resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==} + engines: {node: '>=4'} + dev: true + + /eslint-visitor-keys@2.1.0: + resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} + engines: {node: '>=10'} + dev: true + + /eslint-visitor-keys@3.4.0: + resolution: {integrity: sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint@8.39.0: + resolution: {integrity: sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.39.0) + '@eslint-community/regexpp': 4.5.0 + '@eslint/eslintrc': 2.0.2 + '@eslint/js': 8.39.0 + '@humanwhocodes/config-array': 0.11.8 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.0 + eslint-visitor-keys: 3.4.0 + espree: 9.5.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.20.0 + grapheme-splitter: 1.0.4 + ignore: 5.2.4 + import-fresh: 3.3.0 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-sdsl: 4.4.0 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.1 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree@9.5.1: + resolution: {integrity: sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.8.2 + acorn-jsx: 5.3.2(acorn@8.8.2) + eslint-visitor-keys: 3.4.0 + dev: true + + /esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + /etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + dev: false + + /execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: true + + /exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + dev: true + + /expect@29.6.2: + resolution: {integrity: sha512-iAErsLxJ8C+S02QbLAwgSGSezLQK+XXRDt8IuFXFpwCNw2ECmzZSmjKcCaFVp5VRMk+WAvz6h6jokzEzBFZEuA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/expect-utils': 29.6.2 + '@types/node': 18.16.0 + jest-get-type: 29.4.3 + jest-matcher-utils: 29.6.2 + jest-message-util: 29.6.2 + jest-util: 29.6.2 + dev: true + + /express@4.18.2: + resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} + engines: {node: '>= 0.10.0'} + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.1 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.5.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 + transitivePeerDependencies: + - supports-color + dev: false + + /external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + dev: true + + /extract-files@11.0.0: + resolution: {integrity: sha512-FuoE1qtbJ4bBVvv94CC7s0oTnKUGvQs+Rjf1L2SJFfS+HTVVjhPFtehPdQ0JiGPqVNfSSZvL5yzHHQq2Z4WNhQ==} + engines: {node: ^12.20 || >= 14.13} + dev: true + + /fast-decode-uri-component@1.0.1: + resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-diff@1.2.0: + resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==} + dev: true + + /fast-glob@3.2.12: + resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + /fast-querystring@1.1.1: + resolution: {integrity: sha512-qR2r+e3HvhEFmpdHMv//U8FnFlnYjaC6QKDuaXALDkw2kvHO8WDjxH+f/rHGR4Me4pnk8p9JAkRNTjYHAKRn2Q==} + dependencies: + fast-decode-uri-component: 1.0.1 + dev: true + + /fast-url-parser@1.1.3: + resolution: {integrity: sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==} + dependencies: + punycode: 1.4.1 + dev: true + + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: true + + /fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + dependencies: + bser: 2.1.1 + dev: true + + /fbjs-css-vars@1.0.2: + resolution: {integrity: sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==} + dev: true + + /fbjs@3.0.4: + resolution: {integrity: sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ==} + dependencies: + cross-fetch: 3.1.5 + fbjs-css-vars: 1.0.2 + loose-envify: 1.4.0 + object-assign: 4.1.1 + promise: 7.3.1 + setimmediate: 1.0.5 + ua-parser-js: 0.7.35 + transitivePeerDependencies: + - encoding + dev: true + + /fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + dev: false + + /figures@3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} + dependencies: + escape-string-regexp: 1.0.5 + dev: true + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.0.4 + dev: true + + /filewatcher@3.0.1: + resolution: {integrity: sha512-Fro8py2B8EJupSP37Kyd4kjKZLr+5ksFq7Vbw8A392Z15Unq8016SPUDvO/AsDj5V6bbPk98PTAinpc5YhPbJw==} + dependencies: + debounce: 1.2.1 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /finalhandler@1.2.0: + resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} + engines: {node: '>= 0.8'} + 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 + transitivePeerDependencies: + - supports-color + dev: false + + /find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + dev: true + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache@3.0.4: + resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flatted: 3.2.7 + rimraf: 3.0.2 + dev: true + + /flatted@3.2.7: + resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} + dev: true + + /fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + dev: false + + /for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.7 + dev: true + + /form-data@3.0.1: + resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: false + + /form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: false + + /forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + dev: false + + /fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + dev: false + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /function-bind@1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + + /function.prototype.name@1.1.5: + resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + functions-have-names: 1.2.3 + dev: true + + /functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true + + /gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + dev: true + + /get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + /get-intrinsic@1.2.0: + resolution: {integrity: sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==} + dependencies: + function-bind: 1.1.1 + has: 1.0.3 + has-symbols: 1.0.3 + + /get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + dev: true + + /get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: true + + /get-symbol-description@1.0.0: + resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.0 + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + dev: true + + /globals@13.20.0: + resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globalthis@1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.0 + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.2.12 + ignore: 5.2.4 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.0 + dev: true + + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: true + + /grapheme-splitter@1.0.4: + resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} + dev: true + + /graphql-config@4.5.0(@types/node@18.16.0)(graphql@16.6.0): + resolution: {integrity: sha512-x6D0/cftpLUJ0Ch1e5sj1TZn6Wcxx4oMfmhaG9shM0DKajA9iR+j1z86GSTQ19fShbGvrSSvbIQsHku6aQ6BBw==} + engines: {node: '>= 10.0.0'} + peerDependencies: + cosmiconfig-toml-loader: ^1.0.0 + graphql: ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + peerDependenciesMeta: + cosmiconfig-toml-loader: + optional: true + dependencies: + '@graphql-tools/graphql-file-loader': 7.5.17(graphql@16.6.0) + '@graphql-tools/json-file-loader': 7.4.18(graphql@16.6.0) + '@graphql-tools/load': 7.8.14(graphql@16.6.0) + '@graphql-tools/merge': 8.4.1(graphql@16.6.0) + '@graphql-tools/url-loader': 7.17.18(@types/node@18.16.0)(graphql@16.6.0) + '@graphql-tools/utils': 9.2.1(graphql@16.6.0) + cosmiconfig: 8.0.0 + graphql: 16.6.0 + jiti: 1.17.1 + minimatch: 4.2.3 + string-env-interpolation: 1.0.1 + tslib: 2.5.0 + transitivePeerDependencies: + - '@types/node' + - bufferutil + - encoding + - utf-8-validate + dev: true + + /graphql-request@6.0.0(graphql@16.6.0): + resolution: {integrity: sha512-2BmHTuglonjZvmNVw6ZzCfFlW/qkIPds0f+Qdi/Lvjsl3whJg2uvHmSvHnLWhUTEw6zcxPYAHiZoPvSVKOZ7Jw==} + peerDependencies: + graphql: 14 - 16 + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.6.0) + cross-fetch: 3.1.5 + graphql: 16.6.0 + transitivePeerDependencies: + - encoding + dev: true + + /graphql-tag@2.12.6(graphql@16.6.0): + resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==} + engines: {node: '>=10'} + peerDependencies: + graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + graphql: 16.6.0 + tslib: 2.5.0 + dev: true + + /graphql-ws@5.12.1(graphql@16.6.0): + resolution: {integrity: sha512-umt4f5NnMK46ChM2coO36PTFhHouBrK9stWWBczERguwYrGnPNxJ9dimU6IyOBfOkC6Izhkg4H8+F51W/8CYDg==} + engines: {node: '>=10'} + peerDependencies: + graphql: '>=0.11 <=16' + dependencies: + graphql: 16.6.0 + dev: true + + /graphql@16.6.0: + resolution: {integrity: sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + + /growly@1.3.0: + resolution: {integrity: sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==} + dev: true + + /has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: true + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has-property-descriptors@1.0.0: + resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} + dependencies: + get-intrinsic: 1.2.0 + dev: true + + /has-proto@1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + dev: true + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + /has-tostringtag@1.0.0: + resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /has@1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + + /header-case@2.0.4: + resolution: {integrity: sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==} + dependencies: + capital-case: 1.0.4 + tslib: 2.5.0 + dev: true + + /html-encoding-sniffer@3.0.0: + resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} + engines: {node: '>=12'} + dependencies: + whatwg-encoding: 2.0.0 + dev: false + + /html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + dev: true + + /htmlparser2@8.0.2: + resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.0.1 + entities: 4.5.0 + dev: false + + /http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + dev: false + + /http-proxy-agent@5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + dependencies: + '@tootallnate/once': 2.0.0 + agent-base: 6.0.2 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + + /https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + dependencies: + agent-base: 6.0.2 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + + /human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + dev: true + + /iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + + /iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: false + + /ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + dev: true + + /ignore@5.2.4: + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + dev: true + + /immutable@3.7.6: + resolution: {integrity: sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw==} + engines: {node: '>=0.8.0'} + dev: true + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /import-from@4.0.0: + resolution: {integrity: sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ==} + engines: {node: '>=12.2'} + dev: true + + /import-local@3.1.0: + resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} + engines: {node: '>=8'} + hasBin: true + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + /inquirer@8.2.5: + resolution: {integrity: sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==} + engines: {node: '>=12.0.0'} + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-width: 3.0.0 + external-editor: 3.1.0 + figures: 3.2.0 + lodash: 4.17.21 + mute-stream: 0.0.8 + ora: 5.4.1 + run-async: 2.4.1 + rxjs: 7.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + through: 2.3.8 + wrap-ansi: 7.0.0 + dev: true + + /internal-slot@1.0.5: + resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.0 + has: 1.0.3 + side-channel: 1.0.4 + dev: true + + /invariant@2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + dependencies: + loose-envify: 1.4.0 + dev: true + + /ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + dev: false + + /is-absolute@1.0.0: + resolution: {integrity: sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==} + engines: {node: '>=0.10.0'} + dependencies: + is-relative: 1.0.0 + is-windows: 1.0.2 + dev: true + + /is-array-buffer@3.0.2: + resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.0 + is-typed-array: 1.1.10 + dev: true + + /is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + dev: true + + /is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + dev: false + + /is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.2 + dev: true + + /is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + dev: true + + /is-core-module@2.12.0: + resolution: {integrity: sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==} + dependencies: + has: 1.0.3 + dev: true + + /is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + /is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + dev: true + + /is-lower-case@2.0.2: + resolution: {integrity: sha512-bVcMJy4X5Og6VZfdOZstSexlEy20Sr0k/p/b2IlQJlfdKAQuMpiv5w2Ccxb8sKdRUNAG1PnHVHjFSdRDVS6NlQ==} + dependencies: + tslib: 2.5.0 + dev: true + + /is-negative-zero@2.0.2: + resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} + engines: {node: '>= 0.4'} + dev: true + + /is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + dev: false + + /is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-relative@1.0.0: + resolution: {integrity: sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==} + engines: {node: '>=0.10.0'} + dependencies: + is-unc-path: 1.0.0 + dev: true + + /is-shared-array-buffer@1.0.2: + resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + dependencies: + call-bind: 1.0.2 + dev: true + + /is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + /is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /is-typed-array@1.1.10: + resolution: {integrity: sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + dev: true + + /is-unc-path@1.0.0: + resolution: {integrity: sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==} + engines: {node: '>=0.10.0'} + dependencies: + unc-path-regex: 0.1.2 + dev: true + + /is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + dev: true + + /is-upper-case@2.0.2: + resolution: {integrity: sha512-44pxmxAvnnAOwBg4tHPnkfvgjPwbc5QIsSstNU+YcJ1ovxVzCWpSGosPJOZh/a1tdl81fbgnLc9LLv+x2ywbPQ==} + dependencies: + tslib: 2.5.0 + dev: true + + /is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + dependencies: + call-bind: 1.0.2 + dev: true + + /is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + dev: true + + /is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + dependencies: + is-docker: 2.2.1 + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /isomorphic-ws@5.0.0(ws@8.13.0): + resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} + peerDependencies: + ws: '*' + dependencies: + ws: 8.13.0 + dev: true + + /istanbul-lib-coverage@3.2.0: + resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} + engines: {node: '>=8'} + dev: true + + /istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + dependencies: + '@babel/core': 7.21.5 + '@babel/parser': 7.21.5 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.0 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + dependencies: + istanbul-lib-coverage: 3.2.0 + make-dir: 4.0.0 + supports-color: 7.2.0 + dev: true + + /istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + dependencies: + debug: 4.3.4 + istanbul-lib-coverage: 3.2.0 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-reports@3.1.6: + resolution: {integrity: sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==} + engines: {node: '>=8'} + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + dev: true + + /jest-changed-files@29.5.0: + resolution: {integrity: sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + execa: 5.1.1 + p-limit: 3.1.0 + dev: true + + /jest-circus@29.6.2: + resolution: {integrity: sha512-G9mN+KOYIUe2sB9kpJkO9Bk18J4dTDArNFPwoZ7WKHKel55eKIS/u2bLthxgojwlf9NLCVQfgzM/WsOVvoC6Fw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.6.2 + '@jest/expect': 29.6.2 + '@jest/test-result': 29.6.2 + '@jest/types': 29.6.1 + '@types/node': 18.16.0 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.5.1 + is-generator-fn: 2.1.0 + jest-each: 29.6.2 + jest-matcher-utils: 29.6.2 + jest-message-util: 29.6.2 + jest-runtime: 29.6.2 + jest-snapshot: 29.6.2 + jest-util: 29.6.2 + p-limit: 3.1.0 + pretty-format: 29.6.2 + pure-rand: 6.0.2 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + dev: true + + /jest-cli@29.6.2(@types/node@18.16.0)(ts-node@10.9.1): + resolution: {integrity: sha512-TT6O247v6dCEX2UGHGyflMpxhnrL0DNqP2fRTKYm3nJJpCTfXX3GCMQPGFjXDoj0i5/Blp3jriKXFgdfmbYB6Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.6.2(ts-node@10.9.1) + '@jest/test-result': 29.6.2 + '@jest/types': 29.6.1 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + import-local: 3.1.0 + jest-config: 29.6.2(@types/node@18.16.0)(ts-node@10.9.1) + jest-util: 29.6.2 + jest-validate: 29.6.2 + prompts: 2.4.2 + yargs: 17.7.1 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /jest-config@29.6.2(@types/node@18.16.0)(ts-node@10.9.1): + resolution: {integrity: sha512-VxwFOC8gkiJbuodG9CPtMRjBUNZEHxwfQXmIudSTzFWxaci3Qub1ddTRbFNQlD/zUeaifLndh/eDccFX4wCMQw==} + 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 + dependencies: + '@babel/core': 7.21.5 + '@jest/test-sequencer': 29.6.2 + '@jest/types': 29.6.1 + '@types/node': 18.16.0 + babel-jest: 29.6.2(@babel/core@7.21.5) + chalk: 4.1.2 + ci-info: 3.8.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.6.2 + jest-environment-node: 29.6.2 + jest-get-type: 29.4.3 + jest-regex-util: 29.4.3 + jest-resolve: 29.6.2 + jest-runner: 29.6.2 + jest-util: 29.6.2 + jest-validate: 29.6.2 + micromatch: 4.0.5 + parse-json: 5.2.0 + pretty-format: 29.6.2 + slash: 3.0.0 + strip-json-comments: 3.1.1 + ts-node: 10.9.1(@types/node@18.16.0)(typescript@5.1.6) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + dev: true + + /jest-diff@29.6.2: + resolution: {integrity: sha512-t+ST7CB9GX5F2xKwhwCf0TAR17uNDiaPTZnVymP9lw0lssa9vG+AFyDZoeIHStU3WowFFwT+ky+er0WVl2yGhA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + diff-sequences: 29.4.3 + jest-get-type: 29.4.3 + pretty-format: 29.6.2 + dev: true + + /jest-docblock@29.4.3: + resolution: {integrity: sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + detect-newline: 3.1.0 + dev: true + + /jest-each@29.6.2: + resolution: {integrity: sha512-MsrsqA0Ia99cIpABBc3izS1ZYoYfhIy0NNWqPSE0YXbQjwchyt6B1HD2khzyPe1WiJA7hbxXy77ZoUQxn8UlSw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.1 + chalk: 4.1.2 + jest-get-type: 29.4.3 + jest-util: 29.6.2 + pretty-format: 29.6.2 + dev: true + + /jest-environment-node@29.6.2: + resolution: {integrity: sha512-YGdFeZ3T9a+/612c5mTQIllvWkddPbYcN2v95ZH24oWMbGA4GGS2XdIF92QMhUhvrjjuQWYgUGW2zawOyH63MQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.6.2 + '@jest/fake-timers': 29.6.2 + '@jest/types': 29.6.1 + '@types/node': 18.16.0 + jest-mock: 29.6.2 + jest-util: 29.6.2 + dev: true + + /jest-get-type@29.4.3: + resolution: {integrity: sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /jest-haste-map@29.6.2: + resolution: {integrity: sha512-+51XleTDAAysvU8rT6AnS1ZJ+WHVNqhj1k6nTvN2PYP+HjU3kqlaKQ1Lnw3NYW3bm2r8vq82X0Z1nDDHZMzHVA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.1 + '@types/graceful-fs': 4.1.6 + '@types/node': 18.16.0 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.4.3 + jest-util: 29.6.2 + jest-worker: 29.6.2 + micromatch: 4.0.5 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /jest-leak-detector@29.6.2: + resolution: {integrity: sha512-aNqYhfp5uYEO3tdWMb2bfWv6f0b4I0LOxVRpnRLAeque2uqOVVMLh6khnTcE2qJ5wAKop0HcreM1btoysD6bPQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.4.3 + pretty-format: 29.6.2 + dev: true + + /jest-matcher-utils@29.6.2: + resolution: {integrity: sha512-4LiAk3hSSobtomeIAzFTe+N8kL6z0JtF3n6I4fg29iIW7tt99R7ZcIFW34QkX+DuVrf+CUe6wuVOpm7ZKFJzZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + jest-diff: 29.6.2 + jest-get-type: 29.4.3 + pretty-format: 29.6.2 + dev: true + + /jest-message-util@29.6.2: + resolution: {integrity: sha512-vnIGYEjoPSuRqV8W9t+Wow95SDp6KPX2Uf7EoeG9G99J2OVh7OSwpS4B6J0NfpEIpfkBNHlBZpA2rblEuEFhZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/code-frame': 7.21.4 + '@jest/types': 29.6.1 + '@types/stack-utils': 2.0.1 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.5 + pretty-format: 29.6.2 + slash: 3.0.0 + stack-utils: 2.0.6 + dev: true + + /jest-mock@29.6.2: + resolution: {integrity: sha512-hoSv3lb3byzdKfwqCuT6uTscan471GUECqgNYykg6ob0yiAw3zYc7OrPnI9Qv8Wwoa4lC7AZ9hyS4AiIx5U2zg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.1 + '@types/node': 18.16.0 + jest-util: 29.6.2 + dev: true + + /jest-pnp-resolver@1.2.3(jest-resolve@29.6.2): + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + dependencies: + jest-resolve: 29.6.2 + dev: true + + /jest-regex-util@29.4.3: + resolution: {integrity: sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /jest-resolve-dependencies@29.6.2: + resolution: {integrity: sha512-LGqjDWxg2fuQQm7ypDxduLu/m4+4Lb4gczc13v51VMZbVP5tSBILqVx8qfWcsdP8f0G7aIqByIALDB0R93yL+w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-regex-util: 29.4.3 + jest-snapshot: 29.6.2 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-resolve@29.6.2: + resolution: {integrity: sha512-G/iQUvZWI5e3SMFssc4ug4dH0aZiZpsDq9o1PtXTV1210Ztyb2+w+ZgQkB3iOiC5SmAEzJBOHWz6Hvrd+QnNPw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.6.2 + jest-pnp-resolver: 1.2.3(jest-resolve@29.6.2) + jest-util: 29.6.2 + jest-validate: 29.6.2 + resolve: 1.22.2 + resolve.exports: 2.0.2 + slash: 3.0.0 + dev: true + + /jest-runner@29.6.2: + resolution: {integrity: sha512-wXOT/a0EspYgfMiYHxwGLPCZfC0c38MivAlb2lMEAlwHINKemrttu1uSbcGbfDV31sFaPWnWJPmb2qXM8pqZ4w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.6.2 + '@jest/environment': 29.6.2 + '@jest/test-result': 29.6.2 + '@jest/transform': 29.6.2 + '@jest/types': 29.6.1 + '@types/node': 18.16.0 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.4.3 + jest-environment-node: 29.6.2 + jest-haste-map: 29.6.2 + jest-leak-detector: 29.6.2 + jest-message-util: 29.6.2 + jest-resolve: 29.6.2 + jest-runtime: 29.6.2 + jest-util: 29.6.2 + jest-watcher: 29.6.2 + jest-worker: 29.6.2 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-runtime@29.6.2: + resolution: {integrity: sha512-2X9dqK768KufGJyIeLmIzToDmsN0m7Iek8QNxRSI/2+iPFYHF0jTwlO3ftn7gdKd98G/VQw9XJCk77rbTGZnJg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.6.2 + '@jest/fake-timers': 29.6.2 + '@jest/globals': 29.6.2 + '@jest/source-map': 29.6.0 + '@jest/test-result': 29.6.2 + '@jest/transform': 29.6.2 + '@jest/types': 29.6.1 + '@types/node': 18.16.0 + chalk: 4.1.2 + cjs-module-lexer: 1.2.3 + collect-v8-coverage: 1.0.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.6.2 + jest-message-util: 29.6.2 + jest-mock: 29.6.2 + jest-regex-util: 29.4.3 + jest-resolve: 29.6.2 + jest-snapshot: 29.6.2 + jest-util: 29.6.2 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-snapshot@29.6.2: + resolution: {integrity: sha512-1OdjqvqmRdGNvWXr/YZHuyhh5DeaLp1p/F8Tht/MrMw4Kr1Uu/j4lRG+iKl1DAqUJDWxtQBMk41Lnf/JETYBRA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.21.5 + '@babel/generator': 7.21.5 + '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.21.5) + '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.21.5) + '@babel/types': 7.21.5 + '@jest/expect-utils': 29.6.2 + '@jest/transform': 29.6.2 + '@jest/types': 29.6.1 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.21.5) + chalk: 4.1.2 + expect: 29.6.2 + graceful-fs: 4.2.11 + jest-diff: 29.6.2 + jest-get-type: 29.4.3 + jest-matcher-utils: 29.6.2 + jest-message-util: 29.6.2 + jest-util: 29.6.2 + natural-compare: 1.4.0 + pretty-format: 29.6.2 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-util@29.6.2: + resolution: {integrity: sha512-3eX1qb6L88lJNCFlEADKOkjpXJQyZRiavX1INZ4tRnrBVr2COd3RgcTLyUiEXMNBlDU/cgYq6taUS0fExrWW4w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.1 + '@types/node': 18.16.0 + chalk: 4.1.2 + ci-info: 3.8.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 + dev: true + + /jest-validate@29.6.2: + resolution: {integrity: sha512-vGz0yMN5fUFRRbpJDPwxMpgSXW1LDKROHfBopAvDcmD6s+B/s8WJrwi+4bfH4SdInBA5C3P3BI19dBtKzx1Arg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.1 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.4.3 + leven: 3.1.0 + pretty-format: 29.6.2 + dev: true + + /jest-watcher@29.6.2: + resolution: {integrity: sha512-GZitlqkMkhkefjfN/p3SJjrDaxPflqxEAv3/ik10OirZqJGYH5rPiIsgVcfof0Tdqg3shQGdEIxDBx+B4tuLzA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.6.2 + '@jest/types': 29.6.1 + '@types/node': 18.16.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.6.2 + string-length: 4.0.2 + dev: true + + /jest-worker@29.6.2: + resolution: {integrity: sha512-l3ccBOabTdkng8I/ORCkADz4eSMKejTYv1vB/Z83UiubqhC1oQ5Li6dWCyqOIvSifGjUBxuvxvlm6KGK2DtuAQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@types/node': 18.16.0 + jest-util: 29.6.2 + merge-stream: 2.0.0 + supports-color: 8.1.1 + dev: true + + /jest@29.6.2(@types/node@18.16.0)(ts-node@10.9.1): + resolution: {integrity: sha512-8eQg2mqFbaP7CwfsTpCxQ+sHzw1WuNWL5UUvjnWP4hx2riGz9fPSzYOaU5q8/GqWn1TfgZIVTqYJygbGbWAANg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.6.2(ts-node@10.9.1) + '@jest/types': 29.6.1 + import-local: 3.1.0 + jest-cli: 29.6.2(@types/node@18.16.0)(ts-node@10.9.1) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /jiti@1.17.1: + resolution: {integrity: sha512-NZIITw8uZQFuzQimqjUxIrIcEdxYDFIe/0xYfIlVXTkiBjjyBEvgasj5bb0/cHtPRD/NziPbT312sFrkI5ALpw==} + hasBin: true + dev: true + + /jiti@1.18.2: + resolution: {integrity: sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==} + hasBin: true + dev: true + + /jose@4.14.3: + resolution: {integrity: sha512-YPM9Q+dmsna4CGWNn5+oHFsuXJdxvKAOVoNjpe2nje3odSoX5Xz4s71rP50vM8uUKJyQtMnEGPmbVCVR+G4W5g==} + dev: true + + /js-sdsl@4.4.0: + resolution: {integrity: sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==} + dev: true + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: true + + /js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + dev: true + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /jsdom@21.1.1: + resolution: {integrity: sha512-Jjgdmw48RKcdAIQyUD1UdBh2ecH7VqwaXPN3ehoZN6MqgVbMn+lRm1aAT1AsdJRAJpwfa4IpwgzySn61h2qu3w==} + engines: {node: '>=14'} + peerDependencies: + canvas: ^2.5.0 + peerDependenciesMeta: + canvas: + optional: true + dependencies: + abab: 2.0.6 + acorn: 8.8.2 + acorn-globals: 7.0.1 + cssstyle: 3.0.0 + data-urls: 4.0.0 + decimal.js: 10.4.3 + domexception: 4.0.0 + escodegen: 2.0.0 + form-data: 4.0.0 + html-encoding-sniffer: 3.0.0 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.4 + parse5: 7.1.2 + rrweb-cssom: 0.6.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 4.1.2 + w3c-xmlserializer: 4.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 2.0.0 + whatwg-mimetype: 3.0.0 + whatwg-url: 12.0.1 + ws: 8.13.0 + xml-name-validator: 4.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: false + + /jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /json-stable-stringify@1.0.2: + resolution: {integrity: sha512-eunSSaEnxV12z+Z73y/j5N37/In40GK4GmsSy+tEHJMxknvqnA7/djeYtAgW0GsWHUfg+847WJjKaEylk2y09g==} + dependencies: + jsonify: 0.0.1 + dev: true + + /json-to-pretty-yaml@1.2.2: + resolution: {integrity: sha512-rvm6hunfCcqegwYaG5T4yKJWxc9FXFgBVrcTZ4XfSVRwa5HA/Xs+vB/Eo9treYYHCeNM0nrSUr82V/M31Urc7A==} + engines: {node: '>= 0.2.0'} + dependencies: + remedial: 1.0.8 + remove-trailing-spaces: 1.0.8 + dev: true + + /json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + dependencies: + minimist: 1.2.8 + dev: true + + /json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: true + + /jsonify@0.0.1: + resolution: {integrity: sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==} + dev: true + + /kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + dev: true + + /kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + dev: false + + /leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + dev: true + + /levn@0.3.0: + resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.1.2 + type-check: 0.3.2 + dev: false + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /libphonenumber-js@1.10.28: + resolution: {integrity: sha512-1eAgjLrZA0+2Wgw4hs+4Q/kEBycxQo8ZLYnmOvZ3AlM8ImAVAJgDPlZtISLEzD1vunc2q8s2Pn7XwB7I8U3Kzw==} + dev: false + + /lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true + + /listr2@4.0.5: + resolution: {integrity: sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==} + engines: {node: '>=12'} + peerDependencies: + enquirer: '>= 2.3.0 < 3' + peerDependenciesMeta: + enquirer: + optional: true + dependencies: + cli-truncate: 2.1.0 + colorette: 2.0.20 + log-update: 4.0.0 + p-map: 4.0.0 + rfdc: 1.3.0 + rxjs: 7.8.1 + through: 2.3.8 + wrap-ansi: 7.0.0 + dev: true + + /locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + dependencies: + p-locate: 4.1.0 + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lodash.sortby@4.7.0: + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + dev: false + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true + + /log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + dev: true + + /log-update@4.0.0: + resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} + engines: {node: '>=10'} + dependencies: + ansi-escapes: 4.3.2 + cli-cursor: 3.1.0 + slice-ansi: 4.0.0 + wrap-ansi: 6.2.0 + dev: true + + /logform@2.5.1: + resolution: {integrity: sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==} + dependencies: + '@colors/colors': 1.5.0 + '@types/triple-beam': 1.3.2 + fecha: 4.2.3 + ms: 2.1.3 + safe-stable-stringify: 2.4.3 + triple-beam: 1.3.0 + dev: false + + /loglevel@1.8.1: + resolution: {integrity: sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==} + engines: {node: '>= 0.6.0'} + dev: false + + /long@4.0.0: + resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==} + dev: false + + /loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + dev: true + + /lower-case-first@2.0.2: + resolution: {integrity: sha512-EVm/rR94FJTZi3zefZ82fLWab+GX14LJN4HrWBcuo6Evmsl9hEfnqxgcHCKb9q+mNf6EVdsjx/qucYFIIB84pg==} + dependencies: + tslib: 2.5.0 + dev: true + + /lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + dependencies: + tslib: 2.5.0 + dev: true + + /lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + dependencies: + yallist: 3.1.1 + dev: true + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + dev: false + + /luxon@3.4.0: + resolution: {integrity: sha512-7eDo4Pt7aGhoCheGFIuq4Xa2fJm4ZpmldpGhjTYBNUYNCN6TIEP6v7chwwwt3KRp7YR+rghbfvjyo3V5y9hgBw==} + engines: {node: '>=12'} + dev: false + + /make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + dependencies: + semver: 7.5.4 + dev: true + + /make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true + + /makeerror@1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + dependencies: + tmpl: 1.0.5 + dev: true + + /map-cache@0.2.2: + resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} + engines: {node: '>=0.10.0'} + dev: true + + /media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + dev: false + + /merge-descriptors@1.0.1: + resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + dev: false + + /merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /meros@1.2.1(@types/node@18.16.0): + resolution: {integrity: sha512-R2f/jxYqCAGI19KhAvaxSOxALBMkaXWH2a7rOyqQw+ZmizX5bKkEYWLzdhC+U82ZVVPVp6MCXe3EkVligh+12g==} + engines: {node: '>=13'} + peerDependencies: + '@types/node': '>=13' + peerDependenciesMeta: + '@types/node': + optional: true + dependencies: + '@types/node': 18.16.0 + dev: true + + /methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + dev: false + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + dev: false + + /mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: false + + /mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + dev: false + + /mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimatch@4.2.3: + resolution: {integrity: sha512-lIUdtK5hdofgCTu3aT0sOaHsYR37viUuIc0rwnnDXImbwFRcumyLMeZaM0t0I/fgxS6s6JMfu0rLD1Wz9pv1ng==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true + + /ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + dev: false + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + /mute-stream@0.0.8: + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + dev: true + + /natural-compare-lite@1.4.0: + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} + dev: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + dev: false + + /no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + dependencies: + lower-case: 2.0.2 + tslib: 2.5.0 + dev: true + + /node-abort-controller@3.1.1: + resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} + dev: false + + /node-addon-api@3.2.1: + resolution: {integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==} + dev: true + + /node-dev@8.0.0: + resolution: {integrity: sha512-GXc0KxmBXfQxMPdymOui40yvC5W/RXFwmuUDT65wvTAO/o9wAsddYC8q4EHKxq3Qqt+uLS/g7XKdgVcsjyk9lw==} + engines: {node: '>=14'} + hasBin: true + dependencies: + dateformat: 3.0.3 + dynamic-dedupe: 0.3.0 + filewatcher: 3.0.1 + get-package-type: 0.1.0 + minimist: 1.2.8 + node-notifier: 8.0.2 + resolve: 1.22.2 + semver: 7.5.4 + dev: true + + /node-fetch@2.6.7: + resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + whatwg-url: 5.0.0 + dev: true + + /node-fetch@2.6.9: + resolution: {integrity: sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + whatwg-url: 5.0.0 + + /node-gyp-build@4.6.0: + resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==} + hasBin: true + dev: true + + /node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + dev: true + + /node-notifier@8.0.2: + resolution: {integrity: sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==} + dependencies: + growly: 1.3.0 + is-wsl: 2.2.0 + semver: 7.5.4 + shellwords: 0.1.1 + uuid: 8.3.2 + which: 2.0.2 + dev: true + + /node-releases@2.0.10: + resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==} + dev: true + + /normalize-path@2.1.1: + resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} + engines: {node: '>=0.10.0'} + dependencies: + remove-trailing-separator: 1.1.0 + dev: true + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + dependencies: + path-key: 3.1.1 + dev: true + + /nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + dependencies: + boolbase: 1.0.0 + dev: false + + /nullthrows@1.1.1: + resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} + dev: true + + /nwsapi@2.2.4: + resolution: {integrity: sha512-NHj4rzRo0tQdijE9ZqAx6kYDcoRwYwSYzCA8MY3JzfxlrvEU0jhnhJT9BhqhJs7I/dKcrDm6TyulaRqZPIhN5g==} + dev: false + + /object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + /object-inspect@1.12.3: + resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + + /object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: true + + /object.assign@4.1.4: + resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: true + + /object.values@1.1.6: + resolution: {integrity: sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + dev: true + + /on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + dependencies: + ee-first: 1.1.1 + dev: false + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /one-time@1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + dependencies: + fn.name: 1.1.0 + dev: false + + /onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: true + + /optionator@0.8.3: + resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} + engines: {node: '>= 0.8.0'} + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.3.0 + prelude-ls: 1.1.2 + type-check: 0.3.2 + word-wrap: 1.2.3 + dev: false + + /optionator@0.9.1: + resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} + engines: {node: '>= 0.8.0'} + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.3 + dev: true + + /ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.8.0 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + dev: true + + /os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + dev: true + + /p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + dependencies: + p-try: 2.2.0 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + dependencies: + p-limit: 2.3.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /p-map@4.0.0: + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} + dependencies: + aggregate-error: 3.1.0 + dev: true + + /p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + dev: true + + /param-case@3.0.4: + resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + dependencies: + dot-case: 3.0.4 + tslib: 2.5.0 + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /parse-filepath@1.0.2: + resolution: {integrity: sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==} + engines: {node: '>=0.8'} + dependencies: + is-absolute: 1.0.0 + map-cache: 0.2.2 + path-root: 0.1.1 + dev: true + + /parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + dependencies: + '@babel/code-frame': 7.21.4 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + dev: true + + /parse5-htmlparser2-tree-adapter@7.0.0: + resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==} + dependencies: + domhandler: 5.0.3 + parse5: 7.1.2 + dev: false + + /parse5@7.1.2: + resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} + dependencies: + entities: 4.5.0 + + /parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + dev: false + + /pascal-case@3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + dependencies: + no-case: 3.0.4 + tslib: 2.5.0 + dev: true + + /path-case@3.0.4: + resolution: {integrity: sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==} + dependencies: + dot-case: 3.0.4 + tslib: 2.5.0 + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /path-root-regex@0.1.2: + resolution: {integrity: sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==} + engines: {node: '>=0.10.0'} + dev: true + + /path-root@0.1.1: + resolution: {integrity: sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==} + engines: {node: '>=0.10.0'} + dependencies: + path-root-regex: 0.1.2 + dev: true + + /path-to-regexp@0.1.7: + resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + dev: false + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + dev: true + + /pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + dependencies: + find-up: 4.1.0 + dev: true + + /prelude-ls@1.1.2: + resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} + engines: {node: '>= 0.8.0'} + dev: false + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + dependencies: + fast-diff: 1.2.0 + dev: true + + /prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true + + /pretty-format@29.6.2: + resolution: {integrity: sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.0 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + + /promise@7.3.1: + resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} + dependencies: + asap: 2.0.6 + dev: true + + /prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + dev: true + + /proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + dev: false + + /psl@1.9.0: + resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + dev: false + + /punycode@1.4.1: + resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} + dev: true + + /punycode@2.3.0: + resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} + engines: {node: '>=6'} + + /pure-rand@6.0.2: + resolution: {integrity: sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==} + dev: true + + /pvtsutils@1.3.2: + resolution: {integrity: sha512-+Ipe2iNUyrZz+8K/2IOo+kKikdtfhRKzNpQbruF2URmqPtoqAs8g3xS7TJvFF2GcPXjh7DkqMnpVveRFq4PgEQ==} + dependencies: + tslib: 2.5.0 + dev: true + + /pvutils@1.1.3: + resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==} + engines: {node: '>=6.0.0'} + dev: true + + /qs@6.11.0: + resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.0.4 + dev: false + + /querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + dev: false + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + dev: false + + /raw-body@2.5.1: + resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} + engines: {node: '>= 0.8'} + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + dev: false + + /raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + dev: false + + /react-is@18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + dev: true + + /readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + /regenerator-runtime@0.13.11: + resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} + dev: true + + /regexp.prototype.flags@1.5.0: + resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + functions-have-names: 1.2.3 + dev: true + + /regexpp@3.2.0: + resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} + engines: {node: '>=8'} + dev: true + + /relay-runtime@12.0.0: + resolution: {integrity: sha512-QU6JKr1tMsry22DXNy9Whsq5rmvwr3LSZiiWV/9+DFpuTWvp+WFhobWMc8TC4OjKFfNhEZy7mOiqUAn5atQtug==} + dependencies: + '@babel/runtime': 7.21.5 + fbjs: 3.0.4 + invariant: 2.2.4 + transitivePeerDependencies: + - encoding + dev: true + + /remedial@1.0.8: + resolution: {integrity: sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg==} + dev: true + + /remove-trailing-separator@1.1.0: + resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} + dev: true + + /remove-trailing-spaces@1.0.8: + resolution: {integrity: sha512-O3vsMYfWighyFbTd8hk8VaSj9UAGENxAtX+//ugIst2RMk5e03h6RoIS+0ylsFxY1gvmPuAY/PO4It+gPEeySA==} + dev: true + + /require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + /require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + dev: true + + /requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + dev: false + + /resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + dependencies: + resolve-from: 5.0.0 + dev: true + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + dev: true + + /resolve.exports@2.0.2: + resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} + engines: {node: '>=10'} + dev: true + + /resolve@1.22.2: + resolution: {integrity: sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==} + hasBin: true + dependencies: + is-core-module: 2.12.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + dev: true + + /retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + dev: false + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rfdc@1.3.0: + resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==} + dev: true + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rrweb-cssom@0.6.0: + resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==} + dev: false + + /run-async@2.4.1: + resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} + engines: {node: '>=0.12.0'} + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + dependencies: + tslib: 2.5.0 + dev: true + + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + /safe-regex-test@1.0.0: + resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.0 + is-regex: 1.1.4 + dev: true + + /safe-stable-stringify@2.4.3: + resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} + engines: {node: '>=10'} + dev: false + + /safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + /saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + dependencies: + xmlchars: 2.2.0 + dev: false + + /scuid@1.1.0: + resolution: {integrity: sha512-MuCAyrGZcTLfQoH2XoBlQ8C6bzwN88XT/0slOGz0pn8+gIP85BOAfYa44ZXQUTOwRwPU0QvgU+V+OSajl/59Xg==} + dev: true + + /semver@6.3.0: + resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} + hasBin: true + dev: true + + /semver@7.5.0: + resolution: {integrity: sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /send@0.18.0: + resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} + engines: {node: '>= 0.8.0'} + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + dev: false + + /sentence-case@3.0.4: + resolution: {integrity: sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==} + dependencies: + no-case: 3.0.4 + tslib: 2.5.0 + upper-case-first: 2.0.2 + dev: true + + /serve-static@1.15.0: + resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} + engines: {node: '>= 0.8.0'} + dependencies: + encodeurl: 1.0.2 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.18.0 + transitivePeerDependencies: + - supports-color + dev: false + + /set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + dev: true + + /setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + dev: true + + /setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + dev: false + + /sha.js@2.4.11: + resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} + hasBin: true + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: false + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /shell-quote@1.8.1: + resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + dev: true + + /shellwords@0.1.1: + resolution: {integrity: sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==} + dev: true + + /side-channel@1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.0 + object-inspect: 1.12.3 + + /signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true + + /signedsource@1.0.0: + resolution: {integrity: sha512-6+eerH9fEnNmi/hyM1DXcRK3pWdoMQtlkQ+ns0ntzunjKqp5i3sKCc80ym8Fib3iaYhdJUOPdhlJWj1tvge2Ww==} + dev: true + + /simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + dependencies: + is-arrayish: 0.3.2 + dev: false + + /sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + dev: true + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /slice-ansi@3.0.0: + resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + dev: true + + /slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + dev: true + + /snake-case@3.0.4: + resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + dependencies: + dot-case: 3.0.4 + tslib: 2.5.0 + dev: true + + /source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + + /source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + requiresBuild: true + + /sponge-case@1.0.1: + resolution: {integrity: sha512-dblb9Et4DAtiZ5YSUZHLl4XhH4uK80GhAZrVXdN4O2P4gQ40Wa5UIOPUHlA/nFd2PLblBZWUioLMMAVrgpoYcA==} + dependencies: + tslib: 2.5.0 + dev: true + + /sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + dev: true + + /stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + dev: false + + /stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + dependencies: + escape-string-regexp: 2.0.0 + dev: true + + /statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + dev: false + + /streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + dev: true + + /string-env-interpolation@1.0.1: + resolution: {integrity: sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg==} + dev: true + + /string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + dev: true + + /string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + /string.prototype.trim@1.2.7: + resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + dev: true + + /string.prototype.trimend@1.0.6: + resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + dev: true + + /string.prototype.trimstart@1.0.6: + resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + dev: true + + /string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + dependencies: + safe-buffer: 5.2.1 + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + + /strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + dev: true + + /strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + dev: true + + /strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + dev: true + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /swap-case@2.0.2: + resolution: {integrity: sha512-kc6S2YS/2yXbtkSMunBtKdah4VFETZ8Oh6ONSmSd9bRxhqTrtARUCBUiWXH3xVPpvR7tz2CSnkuXVE42EcGnMw==} + dependencies: + tslib: 2.5.0 + dev: true + + /symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + dev: false + + /test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.2 + dev: true + + /text-hex@1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + dev: false + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + dev: true + + /title-case@3.0.3: + resolution: {integrity: sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==} + dependencies: + tslib: 2.5.0 + dev: true + + /tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + dependencies: + os-tmpdir: 1.0.2 + dev: true + + /tmpl@1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + dev: true + + /to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + dev: true + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + dev: false + + /tough-cookie@4.1.2: + resolution: {integrity: sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==} + engines: {node: '>=6'} + dependencies: + psl: 1.9.0 + punycode: 2.3.0 + universalify: 0.2.0 + url-parse: 1.5.10 + dev: false + + /tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + /tr46@4.1.1: + resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==} + engines: {node: '>=14'} + dependencies: + punycode: 2.3.0 + dev: false + + /triple-beam@1.3.0: + resolution: {integrity: sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==} + dev: false + + /ts-jest@29.1.1(@babel/core@7.21.5)(jest@29.6.2)(typescript@5.1.6): + resolution: {integrity: sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/types': ^29.0.0 + babel-jest: ^29.0.0 + esbuild: '*' + jest: ^29.0.0 + typescript: '>=4.3 <6' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + dependencies: + '@babel/core': 7.21.5 + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + jest: 29.6.2(@types/node@18.16.0)(ts-node@10.9.1) + jest-util: 29.6.2 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.5.4 + typescript: 5.1.6 + yargs-parser: 21.1.1 + dev: true + + /ts-log@2.2.5: + resolution: {integrity: sha512-PGcnJoTBnVGy6yYNFxWVNkdcAuAMstvutN9MgDJIV6L0oG8fB+ZNNy1T+wJzah8RPGor1mZuPQkVfXNDpy9eHA==} + dev: true + + /ts-node@10.9.1(@types/node@18.16.0)(typescript@5.1.6): + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 18.16.0 + acorn: 8.8.2 + acorn-walk: 8.2.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.1.6 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + + /tsconfig-paths@3.14.2: + resolution: {integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==} + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + dev: true + + /tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true + + /tslib@2.5.0: + resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} + + /tsutils@3.21.0(typescript@5.0.4): + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 5.0.4 + dev: true + + /type-check@0.3.2: + resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.1.2 + dev: false + + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + dev: true + + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + dev: true + + /type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + dev: false + + /typed-array-length@1.0.4: + resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + dependencies: + call-bind: 1.0.2 + for-each: 0.3.3 + is-typed-array: 1.1.10 + dev: true + + /typescript@5.0.4: + resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==} + engines: {node: '>=12.20'} + hasBin: true + dev: true + + /typescript@5.1.6: + resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + + /ua-parser-js@0.7.35: + resolution: {integrity: sha512-veRf7dawaj9xaWEu9HoTVn5Pggtc/qj+kqTOFvNiN1l0YdxwC1kvel57UCjThjGa3BHBihE8/UJAHI+uQHmd/g==} + dev: true + + /unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + dependencies: + call-bind: 1.0.2 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + dev: true + + /unc-path-regex@0.1.2: + resolution: {integrity: sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==} + engines: {node: '>=0.10.0'} + dev: true + + /universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + dev: false + + /unixify@1.0.0: + resolution: {integrity: sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==} + engines: {node: '>=0.10.0'} + dependencies: + normalize-path: 2.1.1 + dev: true + + /unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + dev: false + + /update-browserslist-db@1.0.11(browserslist@4.21.5): + resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.21.5 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + + /upper-case-first@2.0.2: + resolution: {integrity: sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==} + dependencies: + tslib: 2.5.0 + dev: true + + /upper-case@2.0.2: + resolution: {integrity: sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==} + dependencies: + tslib: 2.5.0 + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.0 + dev: true + + /url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + dev: false + + /urlpattern-polyfill@8.0.2: + resolution: {integrity: sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ==} + dev: true + + /util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + /utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + dev: false + + /uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + dev: true + + /uuid@9.0.0: + resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} + hasBin: true + dev: false + + /v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: true + + /v8-to-istanbul@9.1.0: + resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} + engines: {node: '>=10.12.0'} + dependencies: + '@jridgewell/trace-mapping': 0.3.18 + '@types/istanbul-lib-coverage': 2.0.4 + convert-source-map: 1.9.0 + dev: true + + /value-or-promise@1.0.12: + resolution: {integrity: sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==} + engines: {node: '>=12'} + + /vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + dev: false + + /w3c-xmlserializer@4.0.0: + resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==} + engines: {node: '>=14'} + dependencies: + xml-name-validator: 4.0.0 + dev: false + + /walker@1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + dependencies: + makeerror: 1.0.12 + dev: true + + /wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + dependencies: + defaults: 1.0.4 + dev: true + + /web-streams-polyfill@3.2.1: + resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==} + engines: {node: '>= 8'} + dev: true + + /webcrypto-core@1.7.7: + resolution: {integrity: sha512-7FjigXNsBfopEj+5DV2nhNpfic2vumtjjgPmeDKk45z+MJwXKKfhPB7118Pfzrmh4jqOMST6Ch37iPAHoImg5g==} + dependencies: + '@peculiar/asn1-schema': 2.3.6 + '@peculiar/json-schema': 1.1.12 + asn1js: 3.0.5 + pvtsutils: 1.3.2 + tslib: 2.5.0 + dev: true + + /webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + /webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + dev: false + + /whatwg-encoding@2.0.0: + resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} + engines: {node: '>=12'} + dependencies: + iconv-lite: 0.6.3 + dev: false + + /whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + dev: false + + /whatwg-url@12.0.1: + resolution: {integrity: sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==} + engines: {node: '>=14'} + dependencies: + tr46: 4.1.1 + webidl-conversions: 7.0.0 + dev: false + + /whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + /which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: true + + /which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + dev: true + + /which-typed-array@1.1.9: + resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + is-typed-array: 1.1.10 + dev: true + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /winston-transport@4.5.0: + resolution: {integrity: sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==} + engines: {node: '>= 6.4.0'} + dependencies: + logform: 2.5.1 + readable-stream: 3.6.2 + triple-beam: 1.3.0 + dev: false + + /winston@3.8.2: + resolution: {integrity: sha512-MsE1gRx1m5jdTTO9Ld/vND4krP2To+lgDoMEHGGa4HIlAUyXJtfc7CxQcGXVyz2IBpw5hbFkj2b/AtUdQwyRew==} + engines: {node: '>= 12.0.0'} + dependencies: + '@colors/colors': 1.5.0 + '@dabh/diagnostics': 2.0.3 + async: 3.2.4 + is-stream: 2.0.1 + logform: 2.5.1 + one-time: 1.0.0 + readable-stream: 3.6.2 + safe-stable-stringify: 2.4.3 + stack-trace: 0.0.10 + triple-beam: 1.3.0 + winston-transport: 4.5.0 + dev: false + + /word-wrap@1.2.3: + resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} + engines: {node: '>=0.10.0'} + + /wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /write-file-atomic@4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + dev: true + + /ws@8.13.0: + resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + /xml-name-validator@4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} + dev: false + + /xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + dev: false + + /xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + dev: true + + /y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + dev: true + + /y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + /yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yaml-ast-parser@0.0.43: + resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==} + dev: true + + /yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + dev: true + + /yaml@2.3.1: + resolution: {integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==} + engines: {node: '>= 14'} + dev: false + + /yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + dev: true + + /yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + /yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + dev: true + + /yargs@17.7.1: + resolution: {integrity: sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==} + engines: {node: '>=12'} + 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.8 + yargs-parser: 21.1.1 + + /yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true + + /zod@3.21.4: + resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==} + dev: false diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..18ec407 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +packages: + - 'packages/*' diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..795ab05 --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "es2022", + "module": "commonjs", + "moduleResolution": "node", + "declaration": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..78c7716 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.base", +} \ No newline at end of file