From 8af35c66408a72267cabfaebdd9d48ed8880b71c Mon Sep 17 00:00:00 2001 From: Yanlong Wang Date: Tue, 11 Feb 2025 18:27:15 +0800 Subject: [PATCH] jina-ai: billing for saas service (#55) * wip: jina billing * wip * fix: build issues * ci: cd gh action * fix: make ci happy --- .dockerignore | 1 + .eslintrc.js | 3 +- .github/workflows/cd.yml | 66 + .vscode/launch.json | 24 + jina-ai/.dockerignore | 1 + jina-ai/Dockerfile | 50 + jina-ai/package-lock.json | 3980 +++++++++++++++++++++++ jina-ai/package.json | 38 + jina-ai/src/dto/jina-embeddings-auth.ts | 347 ++ jina-ai/src/lib/async-context.ts | 9 + jina-ai/src/lib/billing.ts | 102 + jina-ai/src/lib/env-config.ts | 59 + jina-ai/src/lib/errors.ts | 70 + jina-ai/src/lib/firestore.ts | 223 ++ jina-ai/src/lib/logger.ts | 56 + jina-ai/src/lib/registry.ts | 4 + jina-ai/src/patch-express.ts | 93 + jina-ai/src/rate-limit.ts | 278 ++ jina-ai/src/server.ts | 56 + jina-ai/tsconfig.json | 17 + package.json | 5 +- src/__tests__/server.test.ts | 4 +- src/app.ts | 647 ++++ src/server.ts | 636 +--- src/utils/token-tracker.ts | 16 +- tsconfig.json | 12 +- 26 files changed, 6150 insertions(+), 647 deletions(-) create mode 100644 .dockerignore create mode 100644 .github/workflows/cd.yml create mode 100644 .vscode/launch.json create mode 100644 jina-ai/.dockerignore create mode 100644 jina-ai/Dockerfile create mode 100644 jina-ai/package-lock.json create mode 100644 jina-ai/package.json create mode 100644 jina-ai/src/dto/jina-embeddings-auth.ts create mode 100644 jina-ai/src/lib/async-context.ts create mode 100644 jina-ai/src/lib/billing.ts create mode 100644 jina-ai/src/lib/env-config.ts create mode 100644 jina-ai/src/lib/errors.ts create mode 100644 jina-ai/src/lib/firestore.ts create mode 100644 jina-ai/src/lib/logger.ts create mode 100644 jina-ai/src/lib/registry.ts create mode 100644 jina-ai/src/patch-express.ts create mode 100644 jina-ai/src/rate-limit.ts create mode 100644 jina-ai/src/server.ts create mode 100644 jina-ai/tsconfig.json create mode 100644 src/app.ts diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js index df8a10d..dcc14b5 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -16,5 +16,6 @@ module.exports = { rules: { 'no-console': ['error', { allow: ['log', 'error'] }], '@typescript-eslint/no-explicit-any': 'off' - } + }, + ignorePatterns: ["jina-ai/**/*"] }; diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 0000000..a819ef7 --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,66 @@ +run-name: Build push and deploy (CD) +on: + push: + branches: + - main + - ci-debug + tags: + - '*' + +jobs: + build-and-push-to-gcr: + runs-on: ubuntu-latest + concurrency: + group: ${{ github.ref_type == 'branch' && github.ref }} + cancel-in-progress: true + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + with: + lfs: true + - uses: 'google-github-actions/auth@v2' + with: + credentials_json: '${{ secrets.GCLOUD_SERVICE_ACCOUNT_SECRET_JSON }}' + - name: 'Set up Cloud SDK' + uses: 'google-github-actions/setup-gcloud@v2' + - name: "Docker auth" + run: |- + gcloud auth configure-docker us-docker.pkg.dev --quiet + - name: Set controller release version + run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22.12.0 + cache: npm + + - name: npm install + run: npm ci + - name: build application + run: npm run build + - name: Set package version + run: npm version --no-git-tag-version ${{ env.RELEASE_VERSION }} + if: github.ref_type == 'tag' + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: | + us-docker.pkg.dev/research-df067/deepresearch/node-deep-research + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build and push + id: container + uses: docker/build-push-action@v6 + with: + file: jina-ai/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + - name: Deploy with Tag + run: | + gcloud run deploy node-deep-research --image us-docker.pkg.dev/research-df067/deepresearch/node-deep-research@${{steps.container.outputs.imageid}} --tag ${{ env.RELEASE_VERSION }} --region us-central1 --async + \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..8e194c2 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,24 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Attach", + "port": 9229, + "request": "attach", + "skipFiles": [ + "/**" + ], + "type": "node" + }, + { + "name": "Attach by Process ID", + "processId": "${command:PickProcess}", + "request": "attach", + "skipFiles": [ + "/**" + ], + "type": "node" + } + ] + } + \ No newline at end of file diff --git a/jina-ai/.dockerignore b/jina-ai/.dockerignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/jina-ai/.dockerignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/jina-ai/Dockerfile b/jina-ai/Dockerfile new file mode 100644 index 0000000..a584c18 --- /dev/null +++ b/jina-ai/Dockerfile @@ -0,0 +1,50 @@ +# ---- BUILD STAGE ---- +FROM node:20-slim AS builder + +# Set working directory +WORKDIR /app + +# Copy package.json and package-lock.json +COPY ./package*.json ./ +COPY ./jina-ai/package*.json ./jina-ai/ + +# Install dependencies +RUN npm ci +WORKDIR /app/jina-ai +RUN npm ci + +WORKDIR /app + +# Copy application code +COPY ./src ./src +COPY ./config.json ./ +COPY ./tsconfig.json ./tsconfig.json +RUN npm run build + +COPY ./jina-ai/src ./jina-ai/src +COPY ./jina-ai/tsconfig.json ./jina-ai/tsconfig.json +WORKDIR /app/jina-ai +RUN npm run build + +# ---- PRODUCTION STAGE ---- +FROM node:20 AS production + +# Set working directory +WORKDIR /app + +COPY --from=builder /app ./ +# Copy config.json and built files from builder + +WORKDIR /app/jina-ai + +# Set environment variables (Recommended to set at runtime, avoid hardcoding) +ENV GEMINI_API_KEY=${GEMINI_API_KEY} +ENV OPENAI_API_KEY=${OPENAI_API_KEY} +ENV JINA_API_KEY=${JINA_API_KEY} +ENV BRAVE_API_KEY=${BRAVE_API_KEY} + +# Expose the port the app runs on +EXPOSE 3000 + +# Set startup command +CMD ["node", "./dist/server.js"] diff --git a/jina-ai/package-lock.json b/jina-ai/package-lock.json new file mode 100644 index 0000000..9388069 --- /dev/null +++ b/jina-ai/package-lock.json @@ -0,0 +1,3980 @@ +{ + "name": "@jina-ai/node-deepresearch", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@jina-ai/node-deepresearch", + "version": "1.0.0", + "license": "Apache-2.0", + "dependencies": { + "@google-cloud/firestore": "^7.11.0", + "civkit": "^0.8.3-15926cb", + "dayjs": "^1.11.13", + "lodash": "^4.17.21", + "reflect-metadata": "^0.2.2", + "tsyringe": "^4.8.0" + }, + "devDependencies": { + "@types/lodash": "^4.17.15", + "pino-pretty": "^13.0.0" + } + }, + "node_modules/@google-cloud/firestore": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.0.tgz", + "integrity": "sha512-88uZ+jLsp1aVMj7gh3EKYH1aulTAMFAp8sH/v5a9w8q8iqSG27RiWLoxSAFr/XocZ9hGiWH1kEnBw+zl3xAgNA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0", + "fast-deep-equal": "^3.1.1", + "functional-red-black-tree": "^1.0.1", + "google-gax": "^4.3.3", + "protobufjs": "^7.2.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.12.6", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.6.tgz", + "integrity": "sha512-JXUj6PI0oqqzTGvKtzOkxtpsyPRNsrmhh41TtIz/zEB6J+AUiZZ0dxWzcMwO9Ns5rmSPuMdghlTbUuqIM48d3Q==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@hapi/bourne": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-3.0.0.tgz", + "integrity": "sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==", + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@koa/router": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/@koa/router/-/router-12.0.2.tgz", + "integrity": "sha512-sYcHglGKTxGF+hQ6x67xDfkE9o+NhVlRHBqq6gLywaMc6CojK/5vFZByphdonKinYlMLkEkacm+HEse9HzwgTA==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "^4.3.4", + "http-errors": "^2.0.0", + "koa-compose": "^4.1.0", + "methods": "^1.1.2", + "path-to-regexp": "^6.3.0" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.2.0.tgz", + "integrity": "sha512-+ywrb0AqkfaYuhHs6LxKWgqbh3I72EpEgESCw37o+9qPx9WTCkgDm2B+eMrwehGtHBWHFU4GXvnSCNiFhhausg==", + "license": "MIT", + "peer": true, + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "optional": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@peculiar/asn1-cms": { + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.3.15.tgz", + "integrity": "sha512-B+DoudF+TCrxoJSTjjcY8Mmu+lbv8e7pXGWrhNp2/EGJp9EEcpzjBCar7puU57sGifyzaRVM03oD5L7t7PghQg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@peculiar/asn1-schema": "^2.3.15", + "@peculiar/asn1-x509": "^2.3.15", + "@peculiar/asn1-x509-attr": "^2.3.15", + "asn1js": "^3.0.5", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-csr": { + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.3.15.tgz", + "integrity": "sha512-caxAOrvw2hUZpxzhz8Kp8iBYKsHbGXZPl2KYRMIPvAfFateRebS3136+orUpcVwHRmpXWX2kzpb6COlIrqCumA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@peculiar/asn1-schema": "^2.3.15", + "@peculiar/asn1-x509": "^2.3.15", + "asn1js": "^3.0.5", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-ecc": { + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.3.15.tgz", + "integrity": "sha512-/HtR91dvgog7z/WhCVdxZJ/jitJuIu8iTqiyWVgRE9Ac5imt2sT/E4obqIVGKQw7PIy+X6i8lVBoT6wC73XUgA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@peculiar/asn1-schema": "^2.3.15", + "@peculiar/asn1-x509": "^2.3.15", + "asn1js": "^3.0.5", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pfx": { + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.3.15.tgz", + "integrity": "sha512-E3kzQe3J2xV9DP6SJS4X6/N1e4cYa2xOAK46VtvpaRk8jlheNri8v0rBezKFVPB1rz/jW8npO+u1xOvpATFMWg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@peculiar/asn1-cms": "^2.3.15", + "@peculiar/asn1-pkcs8": "^2.3.15", + "@peculiar/asn1-rsa": "^2.3.15", + "@peculiar/asn1-schema": "^2.3.15", + "asn1js": "^3.0.5", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pkcs8": { + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.3.15.tgz", + "integrity": "sha512-/PuQj2BIAw1/v76DV1LUOA6YOqh/UvptKLJHtec/DQwruXOCFlUo7k6llegn8N5BTeZTWMwz5EXruBw0Q10TMg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@peculiar/asn1-schema": "^2.3.15", + "@peculiar/asn1-x509": "^2.3.15", + "asn1js": "^3.0.5", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pkcs9": { + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.3.15.tgz", + "integrity": "sha512-yiZo/1EGvU1KiQUrbcnaPGWc0C7ElMMskWn7+kHsCFm+/9fU0+V1D/3a5oG0Jpy96iaXggQpA9tzdhnYDgjyFg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@peculiar/asn1-cms": "^2.3.15", + "@peculiar/asn1-pfx": "^2.3.15", + "@peculiar/asn1-pkcs8": "^2.3.15", + "@peculiar/asn1-schema": "^2.3.15", + "@peculiar/asn1-x509": "^2.3.15", + "@peculiar/asn1-x509-attr": "^2.3.15", + "asn1js": "^3.0.5", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-rsa": { + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.3.15.tgz", + "integrity": "sha512-p6hsanvPhexRtYSOHihLvUUgrJ8y0FtOM97N5UEpC+VifFYyZa0iZ5cXjTkZoDwxJ/TTJ1IJo3HVTB2JJTpXvg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@peculiar/asn1-schema": "^2.3.15", + "@peculiar/asn1-x509": "^2.3.15", + "asn1js": "^3.0.5", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-schema": { + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.15.tgz", + "integrity": "sha512-QPeD8UA8axQREpgR5UTAfu2mqQmm97oUqahDtNdBcfj3qAnoXzFdQW+aNf/tD2WVXF8Fhmftxoj0eMIT++gX2w==", + "license": "MIT", + "optional": true, + "dependencies": { + "asn1js": "^3.0.5", + "pvtsutils": "^1.3.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-x509": { + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.3.15.tgz", + "integrity": "sha512-0dK5xqTqSLaxv1FHXIcd4Q/BZNuopg+u1l23hT9rOmQ1g4dNtw0g/RnEi+TboB0gOwGtrWn269v27cMgchFIIg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@peculiar/asn1-schema": "^2.3.15", + "asn1js": "^3.0.5", + "pvtsutils": "^1.3.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-x509-attr": { + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.3.15.tgz", + "integrity": "sha512-TWJVJhqc+IS4MTEML3l6W1b0sMowVqdsnI4dnojg96LvTuP8dga9f76fjP07MUuss60uSyT2ckoti/2qHXA10A==", + "license": "MIT", + "optional": true, + "dependencies": { + "@peculiar/asn1-schema": "^2.3.15", + "@peculiar/asn1-x509": "^2.3.15", + "asn1js": "^3.0.5", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/x509": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.12.3.tgz", + "integrity": "sha512-+Mzq+W7cNEKfkNZzyLl6A6ffqc3r21HGZUezgfKxpZrkORfOqgRXnS80Zu0IV6a9Ue9QBJeKD7kN0iWfc3bhRQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@peculiar/asn1-cms": "^2.3.13", + "@peculiar/asn1-csr": "^2.3.13", + "@peculiar/asn1-ecc": "^2.3.14", + "@peculiar/asn1-pkcs9": "^2.3.13", + "@peculiar/asn1-rsa": "^2.3.13", + "@peculiar/asn1-schema": "^2.3.13", + "@peculiar/asn1-x509": "^2.3.13", + "pvtsutils": "^1.3.5", + "reflect-metadata": "^0.2.2", + "tslib": "^2.7.0", + "tsyringe": "^4.8.0" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@types/caseless": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", + "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", + "license": "MIT" + }, + "node_modules/@types/lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz", + "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/request": { + "version": "2.48.12", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", + "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", + "license": "MIT", + "dependencies": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + } + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "license": "MIT" + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/@zxing/text-encoding": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz", + "integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==", + "license": "(Unlicense OR Apache-2.0)", + "optional": true + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "optional": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0", + "optional": true + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT", + "optional": true + }, + "node_modules/asn1js": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz", + "integrity": "sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==", + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "pvtsutils": "^1.3.2", + "pvutils": "^1.1.3", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT", + "optional": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT", + "optional": true + }, + "node_modules/base32.js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.1.0.tgz", + "integrity": "sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/block-stream2": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/block-stream2/-/block-stream2-2.1.0.tgz", + "integrity": "sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg==", + "license": "MIT", + "optional": true, + "dependencies": { + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", + "optional": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "optional": true + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/browser-or-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/browser-or-node/-/browser-or-node-2.1.1.tgz", + "integrity": "sha512-8CVjaLJGuSKMVTxJ2DpBl5XnlNDiT4cQFeuCJJrvJmts9YrTZDizTX7PjC2s6W4x+MBGZeEY6dGMrF04/6Hgqg==", + "license": "MIT", + "optional": true + }, + "node_modules/bson": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.2.tgz", + "integrity": "sha512-5afhLTjqDSA3akH56E+/2J6kTDuSIlBxyXPdQslj9hcIgOUE378xdOfZvC/9q3LifJNI6KR/juZ+d0NRNYBwXg==", + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "optional": true, + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cache-content-type": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz", + "integrity": "sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==", + "license": "MIT", + "optional": true, + "dependencies": { + "mime-types": "^2.1.18", + "ylru": "^1.2.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "license": "MIT", + "optional": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "license": "MIT", + "optional": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "license": "MIT", + "optional": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/civkit": { + "version": "0.8.3-15926cb", + "resolved": "https://registry.npmjs.org/civkit/-/civkit-0.8.3-15926cb.tgz", + "integrity": "sha512-RPPYF0MUcxs4TC7j1IreKDpfFPCYpyg5DG5akDPcSqesbY7mp7zgr5poorD8P6rIWD3Wvu4805RswZi/npnWeQ==", + "license": "AGPL", + "dependencies": { + "lodash": "^4.17.21", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@koa/router": "^12.0.0", + "@nodelib/fs.walk": "^1.2.8", + "@peculiar/x509": "^1.9.3", + "busboy": "^1.6.0", + "express": "^4.18.2", + "iconv-lite": "^0.6.3", + "js-yaml": "^4.1.0", + "jschardet": "^3.0.0", + "koa": "^2.14.2", + "koa-bodyparser": "^4.4.0", + "koa-compose": "^4.1.0", + "libmagic-ffi": "^0.1.4", + "mime": "^3.0.0", + "minimatch": "^10.0.1", + "minio": "^7.0.33", + "node-object-hash": "^3.0.0", + "node-schedule": "^2.1.1", + "nodemailer": "^6.9.1", + "pino": "^8.11.0", + "reflect-metadata": "^0.1.13", + "smtp-server": "^3.11.0", + "tld-extract": "^2.1.0", + "zod": "*", + "zod-openai": "*" + }, + "peerDependencies": { + "mongodb": "^6", + "tsyringe": "^4" + } + }, + "node_modules/civkit/node_modules/reflect-metadata": { + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", + "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", + "license": "Apache-2.0", + "optional": true + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "license": "MIT", + "optional": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/co-body": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/co-body/-/co-body-6.2.0.tgz", + "integrity": "sha512-Kbpv2Yd1NdL1V/V4cwLVxraHDV6K8ayohr2rmH0J87Er8+zJjcTa6dAn9QMPC9CRgU8+aNajKbSf1TzDB1yKPA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@hapi/bourne": "^3.0.0", + "inflation": "^2.0.0", + "qs": "^6.5.2", + "raw-body": "^2.3.3", + "type-is": "^1.6.16" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT", + "optional": true + }, + "node_modules/cookies": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.9.1.tgz", + "integrity": "sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==", + "license": "MIT", + "optional": true, + "dependencies": { + "depd": "~2.0.0", + "keygrip": "~1.1.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/copy-to": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/copy-to/-/copy-to-2.0.1.tgz", + "integrity": "sha512-3DdaFaU/Zf1AnpLiFDeNCD4TOWe3Zl2RZaTzUvWiIk5ERzcCodOE20Vqq4fzCbNoHURFHT4/us/Lfq+S2zyY4w==", + "license": "MIT", + "optional": true + }, + "node_modules/cron-parser": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", + "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "luxon": "^3.2.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==", + "license": "MIT", + "optional": true + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT", + "optional": true + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "optional": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexify": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.2" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT", + "optional": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "optional": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT", + "optional": true + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", + "optional": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "optional": true + }, + "node_modules/express/node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT", + "optional": true + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fast-copy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz", + "integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-redact": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-xml-parser": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.1.tgz", + "integrity": "sha512-y655CeyUQ+jj7KBbYMc4FG01V8ZQqjN+gDYGJ50RtfsUB8iG9AmwmwoAgeKLJdmueKKMrH1RJ7yXHTSoczdv5w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastq": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", + "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", + "license": "ISC", + "optional": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "optional": true + }, + "node_modules/for-each": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.4.tgz", + "integrity": "sha512-kKaIINnFpzW6ffJNDjjyjrk21BkDx38c0xa/klsT8VzLCaMEefv4ZTacrcVR4DmgTeBra++jMDAfS/tS799YDw==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/form-data": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz", + "integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "license": "MIT" + }, + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gcp-metadata": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz", + "integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "gaxios": "^5.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/gcp-metadata/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/gcp-metadata/node_modules/gaxios": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz", + "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/gcp-metadata/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "license": "MIT", + "optional": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "optional": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/google-auth-library": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-auth-library/node_modules/gcp-metadata": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.4.1.tgz", + "integrity": "sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.10.9", + "@grpc/proto-loader": "^0.7.13", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "google-auth-library": "^9.3.0", + "node-fetch": "^2.7.0", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^2.0.2", + "protobufjs": "^7.3.2", + "retry-request": "^7.0.0", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "license": "MIT", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "optional": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz", + "integrity": "sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==", + "license": "MIT", + "optional": true, + "dependencies": { + "deep-equal": "~1.0.1", + "http-errors": "~1.8.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-assert/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-assert/node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "license": "MIT", + "optional": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-assert/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/inflation": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/inflation/-/inflation-2.1.0.tgz", + "integrity": "sha512-t54PPJHG1Pp7VQvxyVCJ9mBbjG3Hqryges9bXoOO6GExCPa+//i/d5GSuFtpx3ALLd7lgIAur6zrIlBQyJuMlQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/ipv6-normalize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ipv6-normalize/-/ipv6-normalize-1.0.1.tgz", + "integrity": "sha512-Bm6H79i01DjgGTCWjUuCjJ6QDo1HB96PT/xCYuyJUP9WFbVDrLSbG4EZCvOCun2rNswZb0c3e4Jt/ws795esHA==", + "license": "MIT", + "optional": true + }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "license": "MIT", + "optional": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "license": "MIT", + "optional": true, + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", + "optional": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jschardet": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-3.1.4.tgz", + "integrity": "sha512-/kmVISmrwVwtyYU40iQUOp3SUPk2dhNCMsZBQX0R1/jZ8maaXJ/oZIzUOiyOqcgtLnETFKYChbJ5iDC/eWmFHg==", + "license": "LGPL-2.1+", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-stream/-/json-stream-1.0.0.tgz", + "integrity": "sha512-H/ZGY0nIAg3QcOwE1QN/rK/Fa7gJn7Ii5obwp6zyPO4xiPNwpIMjqy2gwjBEGqzkF/vSWEIBQCBuN19hYiL6Qg==", + "license": "MIT", + "optional": true + }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keygrip": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", + "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "tsscmp": "1.0.6" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/koa": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/koa/-/koa-2.15.3.tgz", + "integrity": "sha512-j/8tY9j5t+GVMLeioLaxweJiKUayFhlGqNTzf2ZGwL0ZCQijd2RLHK0SLW5Tsko8YyyqCZC2cojIb0/s62qTAg==", + "license": "MIT", + "optional": true, + "dependencies": { + "accepts": "^1.3.5", + "cache-content-type": "^1.0.0", + "content-disposition": "~0.5.2", + "content-type": "^1.0.4", + "cookies": "~0.9.0", + "debug": "^4.3.2", + "delegates": "^1.0.0", + "depd": "^2.0.0", + "destroy": "^1.0.4", + "encodeurl": "^1.0.2", + "escape-html": "^1.0.3", + "fresh": "~0.5.2", + "http-assert": "^1.3.0", + "http-errors": "^1.6.3", + "is-generator-function": "^1.0.7", + "koa-compose": "^4.1.0", + "koa-convert": "^2.0.0", + "on-finished": "^2.3.0", + "only": "~0.0.2", + "parseurl": "^1.3.2", + "statuses": "^1.5.0", + "type-is": "^1.6.16", + "vary": "^1.1.2" + }, + "engines": { + "node": "^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4" + } + }, + "node_modules/koa-bodyparser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/koa-bodyparser/-/koa-bodyparser-4.4.1.tgz", + "integrity": "sha512-kBH3IYPMb+iAXnrxIhXnW+gXV8OTzCu8VPDqvcDHW9SQrbkHmqPQtiZwrltNmSq6/lpipHnT7k7PsjlVD7kK0w==", + "license": "MIT", + "optional": true, + "dependencies": { + "co-body": "^6.0.0", + "copy-to": "^2.0.1", + "type-is": "^1.6.18" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/koa-compose": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz", + "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==", + "license": "MIT", + "optional": true + }, + "node_modules/koa-convert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-2.0.0.tgz", + "integrity": "sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==", + "license": "MIT", + "optional": true, + "dependencies": { + "co": "^4.6.0", + "koa-compose": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/koa/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/koa/node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "license": "MIT", + "optional": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/koa/node_modules/http-errors/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/koa/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/koffi": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/koffi/-/koffi-2.10.1.tgz", + "integrity": "sha512-MjsM1/Xi/p4bjl8hThZ35QpuuWu4MurSDBu+diepnqGMNlUOx/m/hisaw25kr7y4g2GG24TLEOCQgHK3BZvfPg==", + "hasInstallScript": true, + "license": "MIT", + "optional": true + }, + "node_modules/libmagic-ffi": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/libmagic-ffi/-/libmagic-ffi-0.1.4.tgz", + "integrity": "sha512-IVa12E6kLILyjqGX0h0FpGao2gFqls8yvyDx8EMMtXQB/8rhc7ERze+ip8f/WECakWQ7s2c/vRRe6lK4Nzhz9g==", + "license": "MIT", + "optional": true, + "dependencies": { + "generic-pool": "^3.9.0", + "koffi": "^2.9.0", + "tslib": "^2.4.0" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.0.tgz", + "integrity": "sha512-5vvY5yF1zF/kXk+L94FRiTDa1Znom46UjPCH6/XbSvS8zBKMFBHTJk8KDMqJ+2J6QezQFi7k1k8v21ClJYHPaw==", + "license": "Apache-2.0" + }, + "node_modules/long-timeout": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz", + "integrity": "sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==", + "license": "MIT", + "optional": true + }, + "node_modules/luxon": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT", + "peer": true + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "license": "MIT", + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minio": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minio/-/minio-7.1.3.tgz", + "integrity": "sha512-xPrLjWkTT5E7H7VnzOjF//xBp9I40jYB4aWhb2xTFopXXfw+Wo82DDWngdUju7Doy3Wk7R8C4LAgwhLHHnf0wA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "async": "^3.2.4", + "block-stream2": "^2.1.0", + "browser-or-node": "^2.1.1", + "buffer-crc32": "^0.2.13", + "fast-xml-parser": "^4.2.2", + "ipaddr.js": "^2.0.1", + "json-stream": "^1.0.0", + "lodash": "^4.17.21", + "mime-types": "^2.1.35", + "query-string": "^7.1.3", + "through2": "^4.0.2", + "web-encoding": "^1.1.5", + "xml": "^1.0.1", + "xml2js": "^0.5.0" + }, + "engines": { + "node": "^16 || ^18 || >=20" + } + }, + "node_modules/mongodb": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.13.0.tgz", + "integrity": "sha512-KeESYR5TEaFxOuwRqkOm3XOsMqCSkdeDMjaW5u2nuKfX7rqaofp7JQGoi7sVqQcNJTKuveNbzZtWMstb8ABP6Q==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@mongodb-js/saslprep": "^1.1.9", + "bson": "^6.10.1", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/node-object-hash": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-object-hash/-/node-object-hash-3.1.1.tgz", + "integrity": "sha512-A32kRGjXtwQ+uSa3GrXiCl8HVFY0Jy6IiKFO7UjagAKSaOOrruxB2Qf/w7TP5QtNfB3uOiHTu3cjhp8k/C0PCg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=16", + "pnpm": ">=8" + } + }, + "node_modules/node-schedule": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.1.1.tgz", + "integrity": "sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "cron-parser": "^4.2.0", + "long-timeout": "0.1.1", + "sorted-array-functions": "^1.3.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/nodemailer": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.0.tgz", + "integrity": "sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA==", + "license": "MIT-0", + "optional": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "optional": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/only": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz", + "integrity": "sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==", + "optional": true + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "license": "MIT", + "optional": true + }, + "node_modules/pino": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.21.0.tgz", + "integrity": "sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^1.2.0", + "pino-std-serializers": "^6.0.0", + "process-warning": "^3.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^3.7.0", + "thread-stream": "^2.6.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", + "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "readable-stream": "^4.0.0", + "split2": "^4.0.0" + } + }, + "node_modules/pino-abstract-transport/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "optional": true, + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/pino-pretty": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.0.0.tgz", + "integrity": "sha512-cQBBIVG3YajgoUjo1FdKVRX6t9XPxwB9lcNJVD5GCnNM4Y6T12YYx8c6zEejxQsU0wrg9TwmDulcE9LR7qcJqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "colorette": "^2.0.7", + "dateformat": "^4.6.3", + "fast-copy": "^3.0.2", + "fast-safe-stringify": "^2.1.1", + "help-me": "^5.0.0", + "joycon": "^3.1.1", + "minimist": "^1.2.6", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pump": "^3.0.0", + "secure-json-parse": "^2.4.0", + "sonic-boom": "^4.0.1", + "strip-json-comments": "^3.1.1" + }, + "bin": { + "pino-pretty": "bin.js" + } + }, + "node_modules/pino-pretty/node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-pretty/node_modules/sonic-boom": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", + "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", + "dev": true, + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", + "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==", + "license": "MIT", + "optional": true + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==", + "license": "MIT", + "optional": true + }, + "node_modules/proto3-json-serializer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", + "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", + "license": "Apache-2.0", + "dependencies": { + "protobufjs": "^7.2.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "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/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "optional": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pvtsutils": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", + "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/pvutils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.3.tgz", + "integrity": "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/query-string": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", + "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", + "license": "MIT", + "optional": true, + "dependencies": { + "decode-uri-component": "^0.2.2", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT", + "optional": true + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", + "optional": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "license": "Apache-2.0" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/retry-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", + "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", + "license": "MIT", + "dependencies": { + "@types/request": "^2.48.8", + "extend": "^3.0.2", + "teeny-request": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "license": "MIT", + "optional": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "license": "MIT", + "optional": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT", + "optional": true + }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "license": "ISC", + "optional": true + }, + "node_modules/secure-json-parse": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", + "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "optional": true + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", + "optional": true, + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "optional": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC", + "optional": true + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "optional": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "optional": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "optional": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "optional": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/smtp-server": { + "version": "3.13.6", + "resolved": "https://registry.npmjs.org/smtp-server/-/smtp-server-3.13.6.tgz", + "integrity": "sha512-dqbSPKn3PCq3Gp5hxBM99u7PET7cQSAWrauhtArJbc+zrf5xNEOjm9+Ob3lySySrRoIEvNE0dz+w2H/xWFJNRw==", + "license": "MIT-0", + "optional": true, + "dependencies": { + "base32.js": "0.1.0", + "ipv6-normalize": "1.0.1", + "nodemailer": "6.9.15", + "punycode.js": "2.3.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/smtp-server/node_modules/nodemailer": { + "version": "6.9.15", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.15.tgz", + "integrity": "sha512-AHf04ySLC6CIfuRtRiEYtGEXgRfa6INgWGluDhnxTZhHSKvrBu7lc1VVchQ0d8nPc4cFaZoPq8vkyNoZr0TpGQ==", + "license": "MIT-0", + "optional": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/sonic-boom": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.1.tgz", + "integrity": "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==", + "license": "MIT", + "optional": true, + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/sorted-array-functions": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz", + "integrity": "sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==", + "license": "MIT", + "optional": true + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "devOptional": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "license": "MIT", + "dependencies": { + "stubs": "^3.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "license": "MIT" + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "optional": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "license": "MIT", + "optional": true + }, + "node_modules/stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", + "license": "MIT" + }, + "node_modules/teeny-request": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", + "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", + "license": "Apache-2.0", + "dependencies": { + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.9", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/teeny-request/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/teeny-request/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/thread-stream": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz", + "integrity": "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==", + "license": "MIT", + "optional": true, + "dependencies": { + "real-require": "^0.2.0" + } + }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "license": "MIT", + "optional": true, + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/tld-extract": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tld-extract/-/tld-extract-2.1.0.tgz", + "integrity": "sha512-Y9QHWIoDQPJJVm3/pOC7kOfOj7vsNSVZl4JGoEHb605FiwZgIfzSMyU0HC0wYw5Cx8435vaG1yGZtIm1yiQGOw==", + "license": "ISC", + "optional": true + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "license": "MIT", + "peer": true, + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.6.x" + } + }, + "node_modules/tsyringe": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.8.0.tgz", + "integrity": "sha512-YB1FG+axdxADa3ncEtRnQCFq/M0lALGLxSZeVNbTU8NqhOVc51nnv2CISTcvc1kyv6EGPtXVr0v6lWeDxiijOA==", + "license": "MIT", + "dependencies": { + "tslib": "^1.9.3" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/tsyringe/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "optional": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "license": "MIT", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/web-encoding": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/web-encoding/-/web-encoding-1.1.5.tgz", + "integrity": "sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==", + "license": "MIT", + "optional": true, + "dependencies": { + "util": "^0.12.3" + }, + "optionalDependencies": { + "@zxing/text-encoding": "0.9.0" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", + "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", + "license": "MIT", + "peer": true, + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", + "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", + "license": "MIT", + "optional": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", + "license": "MIT", + "optional": true + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "license": "MIT", + "optional": true, + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/ylru": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.4.0.tgz", + "integrity": "sha512-2OQsPNEmBCvXuFlIni/a+Rn+R2pHW9INm0BxXJ4hVDA8TirqMj+J/Rp9ItLatT/5pZqWwefVrTQcHpixsxnVlA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/zod": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", + "license": "MIT", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/jina-ai/package.json b/jina-ai/package.json new file mode 100644 index 0000000..8107124 --- /dev/null +++ b/jina-ai/package.json @@ -0,0 +1,38 @@ +{ + "name": "@jina-ai/node-deepresearch", + "version": "1.0.0", + "main": "dist/app.js", + "files": [ + "dist", + "README.md", + "LICENSE" + ], + "scripts": { + "build": "tsc", + "dev": "npx ts-node src/agent.ts", + "search": "npx ts-node src/test-duck.ts", + "rewrite": "npx ts-node src/tools/query-rewriter.ts", + "lint": "eslint . --ext .ts", + "lint:fix": "eslint . --ext .ts --fix", + "serve": "ts-node src/server.ts", + "eval": "ts-node src/evals/batch-evals.ts", + "test": "jest --testTimeout=30000", + "test:watch": "jest --watch" + }, + "keywords": [], + "author": "Jina AI", + "license": "Apache-2.0", + "description": "", + "dependencies": { + "@google-cloud/firestore": "^7.11.0", + "civkit": "^0.8.3-15926cb", + "dayjs": "^1.11.13", + "lodash": "^4.17.21", + "reflect-metadata": "^0.2.2", + "tsyringe": "^4.8.0" + }, + "devDependencies": { + "@types/lodash": "^4.17.15", + "pino-pretty": "^13.0.0" + } +} diff --git a/jina-ai/src/dto/jina-embeddings-auth.ts b/jina-ai/src/dto/jina-embeddings-auth.ts new file mode 100644 index 0000000..ca3e47b --- /dev/null +++ b/jina-ai/src/dto/jina-embeddings-auth.ts @@ -0,0 +1,347 @@ +import { + Also, AuthenticationFailedError, AuthenticationRequiredError, + DownstreamServiceFailureError, RPC_CALL_ENVIRONMENT, + ArrayOf, AutoCastable, Prop +} from 'civkit/civ-rpc'; +import { parseJSONText } from 'civkit/vectorize'; +import { htmlEscape } from 'civkit/escape'; +import { marshalErrorLike } from 'civkit/lang'; + +import type express from 'express'; + +import logger from '../lib/logger'; +import { AsyncLocalContext } from '../lib/async-context'; +import { InjectProperty } from '../lib/registry'; +import { JinaEmbeddingsDashboardHTTP } from '../lib/billing'; +import envConfig from '../lib/env-config'; + +import { FirestoreRecord } from '../lib/firestore'; +import _ from 'lodash'; +import { RateLimitDesc } from '../rate-limit'; + +export class JinaWallet extends AutoCastable { + @Prop({ + default: '' + }) + user_id!: string; + + @Prop({ + default: 0 + }) + trial_balance!: number; + + @Prop() + trial_start?: Date; + + @Prop() + trial_end?: Date; + + @Prop({ + default: 0 + }) + regular_balance!: number; + + @Prop({ + default: 0 + }) + total_balance!: number; +} + +export class JinaEmbeddingsTokenAccount extends FirestoreRecord { + static override collectionName = 'embeddingsTokenAccounts'; + + override _id!: string; + + @Prop({ + required: true + }) + user_id!: string; + + @Prop({ + nullable: true, + type: String, + }) + email?: string; + + @Prop({ + nullable: true, + type: String, + }) + full_name?: string; + + @Prop({ + nullable: true, + type: String, + }) + customer_id?: string; + + @Prop({ + nullable: true, + type: String, + }) + avatar_url?: string; + + // Not keeping sensitive info for now + // @Prop() + // billing_address?: object; + + // @Prop() + // payment_method?: object; + + @Prop({ + required: true + }) + wallet!: JinaWallet; + + @Prop({ + type: Object + }) + metadata?: { [k: string]: any; }; + + @Prop({ + defaultFactory: () => new Date() + }) + lastSyncedAt!: Date; + + @Prop({ + dictOf: [ArrayOf(RateLimitDesc)] + }) + customRateLimits?: { [k: string]: RateLimitDesc[]; }; + + static patchedFields = [ + ]; + + static override from(input: any) { + for (const field of this.patchedFields) { + if (typeof input[field] === 'string') { + input[field] = parseJSONText(input[field]); + } + } + + return super.from(input) as JinaEmbeddingsTokenAccount; + } + + override degradeForFireStore() { + const copy: any = { + ...this, + wallet: { ...this.wallet }, + // Firebase disability + customRateLimits: _.mapValues(this.customRateLimits, (v) => v.map((x) => ({ ...x }))), + }; + + for (const field of (this.constructor as typeof JinaEmbeddingsTokenAccount).patchedFields) { + if (typeof copy[field] === 'object') { + copy[field] = JSON.stringify(copy[field]) as any; + } + } + + return copy; + } + + [k: string]: any; +} + + +const authDtoLogger = logger.child({ service: 'JinaAuthDTO' }); + +export interface FireBaseHTTPCtx { + req: express.Request, + res: express.Response, +} + +const THE_VERY_SAME_JINA_EMBEDDINGS_CLIENT = new JinaEmbeddingsDashboardHTTP(envConfig.JINA_EMBEDDINGS_DASHBOARD_API_KEY); + +@Also({ + openapi: { + operation: { + parameters: { + 'Authorization': { + description: htmlEscape`Jina Token for authentication.\n\n` + + htmlEscape`- Member of \n\n` + + `- Authorization: Bearer {YOUR_JINA_TOKEN}` + , + in: 'header', + schema: { + anyOf: [ + { type: 'string', format: 'token' } + ] + } + } + } + } + } +}) +export class JinaEmbeddingsAuthDTO extends AutoCastable { + uid?: string; + bearerToken?: string; + user?: JinaEmbeddingsTokenAccount; + + @InjectProperty(AsyncLocalContext) + ctxMgr!: AsyncLocalContext; + + jinaEmbeddingsDashboard = THE_VERY_SAME_JINA_EMBEDDINGS_CLIENT; + + static override from(input: any) { + const instance = super.from(input) as JinaEmbeddingsAuthDTO; + + const ctx = input[RPC_CALL_ENVIRONMENT]; + + const req = (ctx.rawRequest || ctx.req) as express.Request | undefined; + + if (req) { + const authorization = req.get('authorization'); + + if (authorization) { + const authToken = authorization.split(' ')[1] || authorization; + instance.bearerToken = authToken; + } + + } + + if (!instance.bearerToken && input._token) { + instance.bearerToken = input._token; + } + + return instance; + } + + async getBrief(ignoreCache?: boolean | string) { + if (!this.bearerToken) { + throw new AuthenticationRequiredError({ + message: 'Absence of bearer token' + }); + } + + let account; + try { + account = await JinaEmbeddingsTokenAccount.fromFirestore(this.bearerToken); + } catch (err) { + // FireStore would not accept any string as input and may throw if not happy with it + void 0; + } + + + const age = account?.lastSyncedAt ? Date.now() - account.lastSyncedAt.getTime() : Infinity; + + if (account && !ignoreCache) { + if (account && age < 180_000) { + this.user = account; + this.uid = this.user?.user_id; + + return account; + } + } + + try { + const r = await this.jinaEmbeddingsDashboard.validateToken(this.bearerToken); + const brief = r.data; + const draftAccount = JinaEmbeddingsTokenAccount.from({ + ...account, ...brief, _id: this.bearerToken, + lastSyncedAt: new Date() + }); + await JinaEmbeddingsTokenAccount.save(draftAccount.degradeForFireStore(), undefined, { merge: true }); + + this.user = draftAccount; + this.uid = this.user?.user_id; + + return draftAccount; + } catch (err: any) { + authDtoLogger.warn(`Failed to get user brief: ${err}`, { err: marshalErrorLike(err) }); + + if (err?.status === 401) { + throw new AuthenticationFailedError({ + message: 'Invalid bearer token' + }); + } + + if (account) { + this.user = account; + this.uid = this.user?.user_id; + + return account; + } + + + throw new DownstreamServiceFailureError(`Failed to authenticate: ${err}`); + } + } + + async reportUsage(tokenCount: number, mdl: string, endpoint: string = '/encode') { + const user = await this.assertUser(); + const uid = user.user_id; + user.wallet.total_balance -= tokenCount; + + return this.jinaEmbeddingsDashboard.reportUsage(this.bearerToken!, { + model_name: mdl, + api_endpoint: endpoint, + consumer: { + id: uid, + user_id: uid, + }, + usage: { + total_tokens: tokenCount + }, + labels: { + model_name: mdl + } + }).then((r) => { + JinaEmbeddingsTokenAccount.COLLECTION.doc(this.bearerToken!) + .update({ 'wallet.total_balance': JinaEmbeddingsTokenAccount.OPS.increment(-tokenCount) }) + .catch((err) => { + authDtoLogger.warn(`Failed to update cache for ${uid}: ${err}`, { err: marshalErrorLike(err) }); + }); + + return r; + }).catch((err) => { + user.wallet.total_balance += tokenCount; + authDtoLogger.warn(`Failed to report usage for ${uid}: ${err}`, { err: marshalErrorLike(err) }); + }); + } + + async solveUID() { + if (this.uid) { + this.ctxMgr.set('uid', this.uid); + + return this.uid; + } + + if (this.bearerToken) { + await this.getBrief(); + this.ctxMgr.set('uid', this.uid); + + return this.uid; + } + + return undefined; + } + + async assertUID() { + const uid = await this.solveUID(); + + if (!uid) { + throw new AuthenticationRequiredError('Authentication failed'); + } + + return uid; + } + + async assertUser() { + if (this.user) { + return this.user; + } + + await this.getBrief(); + + return this.user!; + } + + getRateLimits(...tags: string[]) { + const descs = tags.map((x) => this.user?.customRateLimits?.[x] || []).flat().filter((x) => x.isEffective()); + + if (descs.length) { + return descs; + } + + return undefined; + } +} diff --git a/jina-ai/src/lib/async-context.ts b/jina-ai/src/lib/async-context.ts new file mode 100644 index 0000000..1d52445 --- /dev/null +++ b/jina-ai/src/lib/async-context.ts @@ -0,0 +1,9 @@ +import { GlobalAsyncContext } from 'civkit/async-context'; +import { container, singleton } from 'tsyringe'; + +@singleton() +export class AsyncLocalContext extends GlobalAsyncContext {} + +const instance = container.resolve(AsyncLocalContext); +Reflect.set(process, 'asyncLocalContext', instance); +export default instance; diff --git a/jina-ai/src/lib/billing.ts b/jina-ai/src/lib/billing.ts new file mode 100644 index 0000000..9c7323a --- /dev/null +++ b/jina-ai/src/lib/billing.ts @@ -0,0 +1,102 @@ +import { HTTPService } from 'civkit'; +import _ from 'lodash'; + + +export interface JinaWallet { + trial_balance: number; + trial_start: Date; + trial_end: Date; + regular_balance: number; + total_balance: number; +} + + +export interface JinaUserBrief { + user_id: string; + email: string | null; + full_name: string | null; + customer_id: string | null; + avatar_url?: string; + billing_address: Partial<{ + address: string; + city: string; + state: string; + country: string; + postal_code: string; + }>; + payment_method: Partial<{ + brand: string; + last4: string; + exp_month: number; + exp_year: number; + }>; + wallet: JinaWallet; + metadata: { + [k: string]: any; + }; +} + +export interface JinaUsageReport { + model_name: string; + api_endpoint: string; + consumer: { + user_id: string; + customer_plan?: string; + [k: string]: any; + }; + usage: { + total_tokens: number; + }; + labels: { + user_type?: string; + model_name?: string; + [k: string]: any; + }; +} + +export class JinaEmbeddingsDashboardHTTP extends HTTPService { + name = 'JinaEmbeddingsDashboardHTTP'; + + constructor( + public apiKey: string, + public baseUri: string = 'https://embeddings-dashboard-api.jina.ai/api' + ) { + super(baseUri); + + this.baseOptions.timeout = 30_000; // 30 sec + } + + async authorization(token: string) { + const r = await this.get('/v1/authorization', { + headers: { + Authorization: `Bearer ${token}` + }, + responseType: 'json', + }); + + return r; + } + + async validateToken(token: string) { + const r = await this.getWithSearchParams('/v1/api_key/user', { + api_key: token, + }, { + responseType: 'json', + }); + + return r; + } + + async reportUsage(token: string, query: JinaUsageReport) { + const r = await this.postJson('/v1/usage', query, { + headers: { + Authorization: `Bearer ${token}`, + 'x-api-key': this.apiKey, + }, + responseType: 'text', + }); + + return r; + } + +} diff --git a/jina-ai/src/lib/env-config.ts b/jina-ai/src/lib/env-config.ts new file mode 100644 index 0000000..d80abbc --- /dev/null +++ b/jina-ai/src/lib/env-config.ts @@ -0,0 +1,59 @@ +import { container, singleton } from 'tsyringe'; + +export const SPECIAL_COMBINED_ENV_KEY = 'ENV_COMBINED'; +const CONF_ENV = [ + 'OPENAI_API_KEY', + + 'ANTHROPIC_API_KEY', + + 'REPLICATE_API_KEY', + + 'GOOGLE_AI_STUDIO_API_KEY', + + 'JINA_EMBEDDINGS_API_KEY', + + 'JINA_EMBEDDINGS_DASHBOARD_API_KEY', + + 'BRAVE_SEARCH_API_KEY', + +] as const; + + +@singleton() +export class EnvConfig { + dynamic!: Record; + + combined: Record = {}; + originalEnv: Record = { ...process.env }; + + constructor() { + if (process.env[SPECIAL_COMBINED_ENV_KEY]) { + Object.assign(this.combined, JSON.parse( + Buffer.from(process.env[SPECIAL_COMBINED_ENV_KEY]!, 'base64').toString('utf-8') + )); + delete process.env[SPECIAL_COMBINED_ENV_KEY]; + } + + // Static config + for (const x of CONF_ENV) { + const s = this.combined[x] || process.env[x] || ''; + Reflect.set(this, x, s); + if (x in process.env) { + delete process.env[x]; + } + } + + // Dynamic config + this.dynamic = new Proxy({ + get: (_target: any, prop: string) => { + return this.combined[prop] || process.env[prop] || ''; + } + }, {}) as any; + } +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface EnvConfig extends Record { } + +const instance = container.resolve(EnvConfig); +export default instance; diff --git a/jina-ai/src/lib/errors.ts b/jina-ai/src/lib/errors.ts new file mode 100644 index 0000000..29d610c --- /dev/null +++ b/jina-ai/src/lib/errors.ts @@ -0,0 +1,70 @@ +import { ApplicationError, Prop, RPC_TRANSFER_PROTOCOL_META_SYMBOL, StatusCode } from 'civkit'; +import _ from 'lodash'; +import dayjs from 'dayjs'; +import utc from 'dayjs/plugin/utc'; + +dayjs.extend(utc); + +@StatusCode(50301) +export class ServiceDisabledError extends ApplicationError { } + +@StatusCode(50302) +export class ServiceCrashedError extends ApplicationError { } + +@StatusCode(50303) +export class ServiceNodeResourceDrainError extends ApplicationError { } + +@StatusCode(40104) +export class EmailUnverifiedError extends ApplicationError { } + +@StatusCode(40201) +export class InsufficientCreditsError extends ApplicationError { } + +@StatusCode(40202) +export class FreeFeatureLimitError extends ApplicationError { } + +@StatusCode(40203) +export class InsufficientBalanceError extends ApplicationError { } + +@StatusCode(40903) +export class LockConflictError extends ApplicationError { } + +@StatusCode(40904) +export class BudgetExceededError extends ApplicationError { } + +@StatusCode(45101) +export class HarmfulContentError extends ApplicationError { } + +@StatusCode(45102) +export class SecurityCompromiseError extends ApplicationError { } + +@StatusCode(41201) +export class BatchSizeTooLargeError extends ApplicationError { } + + +@StatusCode(42903) +export class RateLimitTriggeredError extends ApplicationError { + + @Prop({ + desc: 'Retry after seconds', + }) + retryAfter?: number; + + @Prop({ + desc: 'Retry after date', + }) + retryAfterDate?: Date; + + protected override get [RPC_TRANSFER_PROTOCOL_META_SYMBOL]() { + const retryAfter = this.retryAfter || this.retryAfterDate; + if (!retryAfter) { + return super[RPC_TRANSFER_PROTOCOL_META_SYMBOL]; + } + + return _.merge(_.cloneDeep(super[RPC_TRANSFER_PROTOCOL_META_SYMBOL]), { + headers: { + 'Retry-After': `${retryAfter instanceof Date ? dayjs(retryAfter).utc().format('ddd, DD MMM YYYY HH:mm:ss [GMT]') : retryAfter}`, + } + }); + } +} diff --git a/jina-ai/src/lib/firestore.ts b/jina-ai/src/lib/firestore.ts new file mode 100644 index 0000000..ecaabaa --- /dev/null +++ b/jina-ai/src/lib/firestore.ts @@ -0,0 +1,223 @@ +import _ from 'lodash'; +import { AutoCastable, Prop, RPC_MARSHAL } from 'civkit/civ-rpc'; +import { + Firestore, FieldValue, DocumentReference, + Query, Timestamp, SetOptions, DocumentSnapshot, +} from '@google-cloud/firestore'; + +// Firestore doesn't support JavaScript objects with custom prototypes (i.e. objects that were created via the \"new\" operator) +function patchFireStoreArrogance(func: Function) { + return function (this: unknown) { + const origObjectGetPrototype = Object.getPrototypeOf; + Object.getPrototypeOf = function (x) { + const r = origObjectGetPrototype.call(this, x); + if (!r) { + return r; + } + return Object.prototype; + }; + try { + return func.call(this, ...arguments); + } finally { + Object.getPrototypeOf = origObjectGetPrototype; + } + }; +} + +Reflect.set(DocumentReference.prototype, 'set', patchFireStoreArrogance(Reflect.get(DocumentReference.prototype, 'set'))); +Reflect.set(DocumentSnapshot, 'fromObject', patchFireStoreArrogance(Reflect.get(DocumentSnapshot, 'fromObject'))); + +function mapValuesDeep(v: any, fn: (i: any) => any): any { + if (_.isPlainObject(v)) { + return _.mapValues(v, (i) => mapValuesDeep(i, fn)); + } else if (_.isArray(v)) { + return v.map((i) => mapValuesDeep(i, fn)); + } else { + return fn(v); + } +} + +export type Constructor = { new(...args: any[]): T; }; +export type Constructed = T extends Partial ? U : T extends object ? T : object; + +export function fromFirestore( + this: Constructor, id: string, overrideCollection?: string +): Promise; +export async function fromFirestore( + this: any, id: string, overrideCollection?: string +) { + const collection = overrideCollection || this.collectionName; + if (!collection) { + throw new Error(`Missing collection name to construct ${this.name}`); + } + + const ref = this.DB.collection(overrideCollection || this.collectionName).doc(id); + + const ptr = await ref.get(); + + if (!ptr.exists) { + return undefined; + } + + const doc = this.from( + // Fixes non-native firebase types + mapValuesDeep(ptr.data(), (i: any) => { + if (i instanceof Timestamp) { + return i.toDate(); + } + + return i; + }) + ); + + Object.defineProperty(doc, '_ref', { value: ref, enumerable: false }); + Object.defineProperty(doc, '_id', { value: ptr.id, enumerable: true }); + + return doc; +} + +export function fromFirestoreQuery( + this: Constructor, query: Query +): Promise; +export async function fromFirestoreQuery(this: any, query: Query) { + const ptr = await query.get(); + + if (ptr.docs.length) { + return ptr.docs.map(doc => { + const r = this.from( + mapValuesDeep(doc.data(), (i: any) => { + if (i instanceof Timestamp) { + return i.toDate(); + } + + return i; + }) + ); + Object.defineProperty(r, '_ref', { value: doc.ref, enumerable: false }); + Object.defineProperty(r, '_id', { value: doc.id, enumerable: true }); + + return r; + }); + } + + return []; +} + +export function setToFirestore( + this: Constructor, doc: T, overrideCollection?: string, setOptions?: SetOptions +): Promise; +export async function setToFirestore( + this: any, doc: any, overrideCollection?: string, setOptions?: SetOptions +) { + let ref: DocumentReference = doc._ref; + if (!ref) { + const collection = overrideCollection || this.collectionName; + if (!collection) { + throw new Error(`Missing collection name to construct ${this.name}`); + } + + const predefinedId = doc._id || undefined; + const hdl = this.DB.collection(overrideCollection || this.collectionName); + ref = predefinedId ? hdl.doc(predefinedId) : hdl.doc(); + + Object.defineProperty(doc, '_ref', { value: ref, enumerable: false }); + Object.defineProperty(doc, '_id', { value: ref.id, enumerable: true }); + } + + await ref.set(doc, { merge: true, ...setOptions }); + + return doc; +} + +export function deleteQueryBatch( + this: Constructor, query: Query +): Promise; +export async function deleteQueryBatch(this: any, query: Query) { + const snapshot = await query.get(); + + const batchSize = snapshot.size; + if (batchSize === 0) { + return; + } + + // Delete documents in a batch + const batch = this.DB.batch(); + snapshot.docs.forEach((doc) => { + batch.delete(doc.ref); + }); + await batch.commit(); + + process.nextTick(() => { + this.deleteQueryBatch(query); + }); +}; + +export function fromFirestoreDoc( + this: Constructor, snapshot: DocumentSnapshot, +): T | undefined; +export function fromFirestoreDoc( + this: any, snapshot: DocumentSnapshot, +) { + const doc = this.from( + // Fixes non-native firebase types + mapValuesDeep(snapshot.data(), (i: any) => { + if (i instanceof Timestamp) { + return i.toDate(); + } + + return i; + }) + ); + + Object.defineProperty(doc, '_ref', { value: snapshot.ref, enumerable: false }); + Object.defineProperty(doc, '_id', { value: snapshot.id, enumerable: true }); + + return doc; +} +const defaultFireStore = new Firestore({ + projectId: process.env.GCLOUD_PROJECT, +}); +export class FirestoreRecord extends AutoCastable { + static collectionName?: string; + static OPS = FieldValue; + static DB = defaultFireStore; + static get COLLECTION() { + if (!this.collectionName) { + throw new Error('Not implemented'); + } + + return this.DB.collection(this.collectionName); + } + + @Prop() + _id?: string; + _ref?: DocumentReference>>; + + static fromFirestore = fromFirestore; + static fromFirestoreDoc = fromFirestoreDoc; + static fromFirestoreQuery = fromFirestoreQuery; + + static save = setToFirestore; + static deleteQueryBatch = deleteQueryBatch; + + [RPC_MARSHAL]() { + return { + ...this, + _id: this._id, + _ref: this._ref?.path + }; + } + + degradeForFireStore(): this { + return JSON.parse(JSON.stringify(this, function (k, v) { + if (k === '') { + return v; + } + if (typeof v === 'object' && v && (typeof v.degradeForFireStore === 'function')) { + return v.degradeForFireStore(); + } + + return v; + })); + } +} diff --git a/jina-ai/src/lib/logger.ts b/jina-ai/src/lib/logger.ts new file mode 100644 index 0000000..af63125 --- /dev/null +++ b/jina-ai/src/lib/logger.ts @@ -0,0 +1,56 @@ +import { AbstractPinoLogger } from 'civkit/pino-logger'; +import { singleton, container } from 'tsyringe'; +import { threadId } from 'node:worker_threads'; +import { getTraceCtx } from 'civkit/async-context'; + + +const levelToSeverityMap: { [k: string]: string | undefined; } = { + trace: 'DEFAULT', + debug: 'DEBUG', + info: 'INFO', + warn: 'WARNING', + error: 'ERROR', + fatal: 'CRITICAL', +}; + +@singleton() +export class GlobalLogger extends AbstractPinoLogger { + loggerOptions = { + level: 'debug', + base: { + tid: threadId, + } + }; + + override init(): void { + if (process.env['NODE_ENV']?.startsWith('prod')) { + super.init(process.stdout); + } else { + const PinoPretty = require('pino-pretty').PinoPretty; + super.init(PinoPretty({ + singleLine: true, + colorize: true, + messageFormat(log: any, messageKey: any) { + return `${log['tid'] ? `[${log['tid']}]` : ''}[${log['service'] || 'ROOT'}] ${log[messageKey]}`; + }, + })); + } + + + this.emit('ready'); + } + + override log(...args: any[]) { + const [levelObj, ...rest] = args; + const severity = levelToSeverityMap[levelObj?.level]; + const traceCtx = getTraceCtx(); + const patched: any= { ...levelObj, severity }; + if (traceCtx?.traceId && process.env['GCLOUD_PROJECT']) { + patched['logging.googleapis.com/trace'] = `projects/${process.env['GCLOUD_PROJECT']}/traces/${traceCtx.traceId}`; + } + return super.log(patched, ...rest); + } +} + +const instance = container.resolve(GlobalLogger); +export default instance; diff --git a/jina-ai/src/lib/registry.ts b/jina-ai/src/lib/registry.ts new file mode 100644 index 0000000..66120b2 --- /dev/null +++ b/jina-ai/src/lib/registry.ts @@ -0,0 +1,4 @@ +import { container } from 'tsyringe'; +import { propertyInjectorFactory } from 'civkit/property-injector'; + +export const InjectProperty = propertyInjectorFactory(container); \ No newline at end of file diff --git a/jina-ai/src/patch-express.ts b/jina-ai/src/patch-express.ts new file mode 100644 index 0000000..a91815f --- /dev/null +++ b/jina-ai/src/patch-express.ts @@ -0,0 +1,93 @@ +import { ApplicationError, RPC_CALL_ENVIRONMENT } from "civkit/civ-rpc"; +import { marshalErrorLike } from "civkit/lang"; +import { randomUUID } from "crypto"; +import { once } from "events"; +import type { NextFunction, Request, Response } from "express"; + +import { JinaEmbeddingsAuthDTO } from "./dto/jina-embeddings-auth"; +import rateLimitControl, { API_CALL_STATUS, RateLimitDesc } from "./rate-limit"; +import asyncLocalContext from "./lib/async-context"; +import globalLogger from "./lib/logger"; + +globalLogger.serviceReady(); +const logger = globalLogger.child({ service: 'BillingMiddleware' }); + +const appName = 'DEEPRESEARCH'; +export const jinaAiBillingMiddleware = (req: Request, res: Response, next: NextFunction) => { + if (req.path === '/ping') { + res.status(200).end('pone'); + return; + } + if (req.method !== 'POST' && req.method !== 'GET') { + next(); + return; + } + asyncLocalContext.run(async () => { + const googleTraceId = req.get('x-cloud-trace-context')?.split('/')?.[0]; + const ctx = asyncLocalContext.ctx; + ctx.traceId = req.get('x-request-id') || req.get('request-id') || googleTraceId || randomUUID(); + ctx.traceT0 = new Date(); + ctx.ip = req?.ip; + + try { + const authDto = JinaEmbeddingsAuthDTO.from({ + [RPC_CALL_ENVIRONMENT]: { req, res } + }); + + const user = await authDto.assertUser(); + await rateLimitControl.serviceReady(); + const rateLimitPolicy = authDto.getRateLimits(appName) || [ + parseInt(user.metadata?.speed_level) >= 2 ? + RateLimitDesc.from({ + occurrence: 30, + periodSeconds: 60 + }) : + RateLimitDesc.from({ + occurrence: 10, + periodSeconds: 60 + }) + ]; + const criterions = rateLimitPolicy.map((c) => rateLimitControl.rateLimitDescToCriterion(c)); + await Promise.all(criterions.map(([pointInTime, n]) => rateLimitControl.assertUidPeriodicLimit(user._id, pointInTime, n, appName))); + + const apiRoll = rateLimitControl.record({ uid: user._id, tags: [appName] }) + apiRoll.save().catch((err) => logger.warn(`Failed to save rate limit record`, { err: marshalErrorLike(err) })); + + const pResClose = once(res, 'close'); + + next(); + + await pResClose; + const chargeAmount = ctx.chargeAmount; + if (chargeAmount) { + authDto.reportUsage(chargeAmount, `reader-${appName}`).catch((err) => { + logger.warn(`Unable to report usage for ${user._id}`, { err: marshalErrorLike(err) }); + }); + apiRoll.chargeAmount = chargeAmount; + } + apiRoll.status = res.statusCode === 200 ? API_CALL_STATUS.SUCCESS : API_CALL_STATUS.ERROR; + apiRoll.save().catch((err) => logger.warn(`Failed to save rate limit record`, { err: marshalErrorLike(err) })); + logger.info(`HTTP ${res.statusCode} for request ${ctx.traceId} after ${Date.now() - ctx.traceT0.valueOf()}ms`, { + uid: user._id, + chargeAmount, + }); + + } catch (err: any) { + if (!res.headersSent) { + if (err instanceof ApplicationError) { + res.status(parseInt(err.code as string) || 500).json({ error: err.message }); + + return; + } + + res.status(500).json({ error: 'Internal' }); + } + + logger.error(`Error in billing middleware`, { err: marshalErrorLike(err) }); + if (err.stack) { + logger.error(err.stack); + } + } + + }); +} \ No newline at end of file diff --git a/jina-ai/src/rate-limit.ts b/jina-ai/src/rate-limit.ts new file mode 100644 index 0000000..283d545 --- /dev/null +++ b/jina-ai/src/rate-limit.ts @@ -0,0 +1,278 @@ +import { AutoCastable, ResourcePolicyDenyError, Also, Prop } from 'civkit/civ-rpc'; +import { AsyncService } from 'civkit/async-service'; +import { getTraceId } from 'civkit/async-context'; +import { singleton, container } from 'tsyringe'; + +import { RateLimitTriggeredError } from './lib/errors'; +import { FirestoreRecord } from './lib/firestore'; +import { GlobalLogger } from './lib/logger'; + +export enum API_CALL_STATUS { + SUCCESS = 'success', + ERROR = 'error', + PENDING = 'pending', +} + +@Also({ dictOf: Object }) +export class APICall extends FirestoreRecord { + static override collectionName = 'apiRoll'; + + @Prop({ + required: true, + defaultFactory: () => getTraceId() + }) + traceId!: string; + + @Prop() + uid?: string; + + @Prop() + ip?: string; + + @Prop({ + arrayOf: String, + default: [], + }) + tags!: string[]; + + @Prop({ + required: true, + defaultFactory: () => new Date(), + }) + createdAt!: Date; + + @Prop() + completedAt?: Date; + + @Prop({ + required: true, + default: API_CALL_STATUS.PENDING, + }) + status!: API_CALL_STATUS; + + @Prop({ + required: true, + defaultFactory: () => new Date(Date.now() + 1000 * 60 * 60 * 24 * 90), + }) + expireAt!: Date; + + [k: string]: any; + + tag(...tags: string[]) { + for (const t of tags) { + if (!this.tags.includes(t)) { + this.tags.push(t); + } + } + } + + save() { + return (this.constructor as typeof APICall).save(this); + } +} + + +export class RateLimitDesc extends AutoCastable { + @Prop({ + default: 1000 + }) + occurrence!: number; + + @Prop({ + default: 3600 + }) + periodSeconds!: number; + + @Prop() + notBefore?: Date; + + @Prop() + notAfter?: Date; + + isEffective() { + const now = new Date(); + if (this.notBefore && this.notBefore > now) { + return false; + } + if (this.notAfter && this.notAfter < now) { + return false; + } + + return true; + } +} + + +@singleton() +export class RateLimitControl extends AsyncService { + + logger = this.globalLogger.child({ service: this.constructor.name }); + + constructor( + protected globalLogger: GlobalLogger, + ) { + super(...arguments); + } + + override async init() { + await this.dependencyReady(); + + this.emit('ready'); + } + + async queryByUid(uid: string, pointInTime: Date, ...tags: string[]) { + let q = APICall.COLLECTION + .orderBy('createdAt', 'asc') + .where('createdAt', '>=', pointInTime) + .where('status', 'in', [API_CALL_STATUS.SUCCESS, API_CALL_STATUS.PENDING]) + .where('uid', '==', uid); + if (tags.length) { + q = q.where('tags', 'array-contains-any', tags); + } + + return APICall.fromFirestoreQuery(q); + } + + async queryByIp(ip: string, pointInTime: Date, ...tags: string[]) { + let q = APICall.COLLECTION + .orderBy('createdAt', 'asc') + .where('createdAt', '>=', pointInTime) + .where('status', 'in', [API_CALL_STATUS.SUCCESS, API_CALL_STATUS.PENDING]) + .where('ip', '==', ip); + if (tags.length) { + q = q.where('tags', 'array-contains-any', tags); + } + + return APICall.fromFirestoreQuery(q); + } + + async assertUidPeriodicLimit(uid: string, pointInTime: Date, limit: number, ...tags: string[]) { + if (limit <= 0) { + throw new ResourcePolicyDenyError(`This UID(${uid}) is not allowed to call this endpoint (rate limit quota is 0).`); + } + + let q = APICall.COLLECTION + .orderBy('createdAt', 'asc') + .where('createdAt', '>=', pointInTime) + .where('status', 'in', [API_CALL_STATUS.SUCCESS, API_CALL_STATUS.PENDING]) + .where('uid', '==', uid); + if (tags.length) { + q = q.where('tags', 'array-contains-any', tags); + } + const count = (await q.count().get()).data().count; + + if (count >= limit) { + const r = await APICall.fromFirestoreQuery(q.limit(1)); + const [r1] = r; + + const dtMs = Math.abs(r1.createdAt?.valueOf() - pointInTime.valueOf()); + const dtSec = Math.ceil(dtMs / 1000); + + throw RateLimitTriggeredError.from({ + message: `Per UID rate limit exceeded (${tags.join(',') || 'called'} ${limit} times since ${pointInTime})`, + retryAfter: dtSec, + }); + } + + return count + 1; + } + + async assertIPPeriodicLimit(ip: string, pointInTime: Date, limit: number, ...tags: string[]) { + let q = APICall.COLLECTION + .orderBy('createdAt', 'asc') + .where('createdAt', '>=', pointInTime) + .where('status', 'in', [API_CALL_STATUS.SUCCESS, API_CALL_STATUS.PENDING]) + .where('ip', '==', ip); + if (tags.length) { + q = q.where('tags', 'array-contains-any', tags); + } + + const count = (await q.count().get()).data().count; + + if (count >= limit) { + const r = await APICall.fromFirestoreQuery(q.limit(1)); + const [r1] = r; + + const dtMs = Math.abs(r1.createdAt?.valueOf() - pointInTime.valueOf()); + const dtSec = Math.ceil(dtMs / 1000); + + throw RateLimitTriggeredError.from({ + message: `Per IP rate limit exceeded (${tags.join(',') || 'called'} ${limit} times since ${pointInTime})`, + retryAfter: dtSec, + }); + } + + return count + 1; + } + + record(partialRecord: Partial) { + const record = APICall.from(partialRecord); + const newId = APICall.COLLECTION.doc().id; + record._id = newId; + + return record; + } + + // async simpleRPCUidBasedLimit(rpcReflect: RPCReflection, uid: string, tags: string[] = [], + // ...inputCriterion: RateLimitDesc[] | [Date, number][]) { + // const criterion = inputCriterion.map((c) => { return Array.isArray(c) ? c : this.rateLimitDescToCriterion(c); }); + + // await Promise.all(criterion.map(([pointInTime, n]) => + // this.assertUidPeriodicLimit(uid, pointInTime, n, ...tags))); + + // const r = this.record({ + // uid, + // tags, + // }); + + // r.save().catch((err) => this.logger.warn(`Failed to save rate limit record`, { err })); + // rpcReflect.then(() => { + // r.status = API_CALL_STATUS.SUCCESS; + // r.save() + // .catch((err) => this.logger.warn(`Failed to save rate limit record`, { err })); + // }); + // rpcReflect.catch((err) => { + // r.status = API_CALL_STATUS.ERROR; + // r.error = err.toString(); + // r.save() + // .catch((err) => this.logger.warn(`Failed to save rate limit record`, { err })); + // }); + + // return r; + // } + + rateLimitDescToCriterion(rateLimitDesc: RateLimitDesc) { + return [new Date(Date.now() - rateLimitDesc.periodSeconds * 1000), rateLimitDesc.occurrence] as [Date, number]; + } + + // async simpleRpcIPBasedLimit(rpcReflect: RPCReflection, ip: string, tags: string[] = [], + // ...inputCriterion: RateLimitDesc[] | [Date, number][]) { + // const criterion = inputCriterion.map((c) => { return Array.isArray(c) ? c : this.rateLimitDescToCriterion(c); }); + // await Promise.all(criterion.map(([pointInTime, n]) => + // this.assertIPPeriodicLimit(ip, pointInTime, n, ...tags))); + + // const r = this.record({ + // ip, + // tags, + // }); + + // r.save().catch((err) => this.logger.warn(`Failed to save rate limit record`, { err })); + // rpcReflect.then(() => { + // r.status = API_CALL_STATUS.SUCCESS; + // r.save() + // .catch((err) => this.logger.warn(`Failed to save rate limit record`, { err })); + // }); + // rpcReflect.catch((err) => { + // r.status = API_CALL_STATUS.ERROR; + // r.error = err.toString(); + // r.save() + // .catch((err) => this.logger.warn(`Failed to save rate limit record`, { err })); + // }); + + // return r; + // } +} + +const instance = container.resolve(RateLimitControl); + +export default instance; diff --git a/jina-ai/src/server.ts b/jina-ai/src/server.ts new file mode 100644 index 0000000..fdf31c2 --- /dev/null +++ b/jina-ai/src/server.ts @@ -0,0 +1,56 @@ +import 'reflect-metadata' +import express from 'express'; +import { jinaAiBillingMiddleware } from "./patch-express"; +import { Server } from 'http'; + +const app = require('../..').default; + +const rootApp = express(); +rootApp.use(jinaAiBillingMiddleware, app); + + +const port = process.env.PORT || 3000; + +let server: Server | undefined; +// Export server startup function for better testing +export function startServer() { + return rootApp.listen(port, () => { + console.log(`Server running at http://localhost:${port}`); + }); +} + +// Start server if running directly +if (process.env.NODE_ENV !== 'test') { + server = startServer(); +} + +process.on('unhandledRejection', (_err) => `Is false alarm`); + +process.on('uncaughtException', (err) => { + console.log('Uncaught exception', err); + + // Looks like Firebase runtime does not handle error properly. + // Make sure to quit the process. + process.nextTick(() => process.exit(1)); + console.error('Uncaught exception, process quit.'); + throw err; +}); + +const sigHandler = (signal: string) => { + console.log(`Received ${signal}, exiting...`); + if (server && server.listening) { + console.log(`Shutting down gracefully...`); + console.log(`Waiting for the server to drain and close...`); + server.close((err) => { + if (err) { + console.error('Error while closing server', err); + return; + } + process.exit(0); + }); + server.closeIdleConnections(); + } + +} +process.on('SIGTERM', sigHandler); +process.on('SIGINT', sigHandler); \ No newline at end of file diff --git a/jina-ai/tsconfig.json b/jina-ai/tsconfig.json new file mode 100644 index 0000000..117917e --- /dev/null +++ b/jina-ai/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "node16", + "outDir": "./dist", + "rootDir": "./src", + "sourceMap": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "resolveJsonModule": true + } + } + \ No newline at end of file diff --git a/package.json b/package.json index 93e6d89..a485e04 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,13 @@ { "name": "node-deepresearch", "version": "1.0.0", - "main": "dist/index.js", - "types": "dist/index.d.ts", + "main": "dist/app.js", "files": [ "dist", "README.md", "LICENSE" ], "scripts": { - "prepare": "npm run build", "build": "tsc", "dev": "npx ts-node src/agent.ts", "search": "npx ts-node src/test-duck.ts", @@ -17,6 +15,7 @@ "lint": "eslint . --ext .ts", "lint:fix": "eslint . --ext .ts --fix", "serve": "ts-node src/server.ts", + "start": "ts-node src/server.ts", "eval": "ts-node src/evals/batch-evals.ts", "test": "jest --testTimeout=30000", "test:watch": "jest --watch" diff --git a/src/__tests__/server.test.ts b/src/__tests__/server.test.ts index f22c43b..312a1b9 100644 --- a/src/__tests__/server.test.ts +++ b/src/__tests__/server.test.ts @@ -22,7 +22,7 @@ describe('/v1/chat/completions', () => { process.argv.push(`--secret=${TEST_SECRET}`); // Import server module (jest.resetModules() is called automatically before each test) - const { default: serverModule } = await import('../server'); + const { default: serverModule } = await require('../app'); app = serverModule; }); @@ -67,7 +67,7 @@ describe('/v1/chat/completions', () => { jest.resetModules(); // Reload server module without secret - const { default: serverModule } = await import('../server'); + const { default: serverModule } = await require('../app'); app = serverModule; const response = await request(app) diff --git a/src/app.ts b/src/app.ts new file mode 100644 index 0000000..bd74137 --- /dev/null +++ b/src/app.ts @@ -0,0 +1,647 @@ +import express, {Request, Response, RequestHandler} from 'express'; +import cors from 'cors'; +import {EventEmitter} from 'events'; +import {getResponse} from './agent'; +import { + StepAction, + StreamMessage, + TrackerContext, + ChatCompletionRequest, + ChatCompletionResponse, + ChatCompletionChunk, + AnswerAction, + TOKEN_CATEGORIES, + Model +} from './types'; +import fs from 'fs/promises'; +import path from 'path'; +import {TokenTracker} from "./utils/token-tracker"; +import {ActionTracker} from "./utils/action-tracker"; + +const app = express(); + +// Get secret from command line args for optional authentication +const secret = process.argv.find(arg => arg.startsWith('--secret='))?.split('=')[1]; + +app.use(cors()); +app.use(express.json()); + +const eventEmitter = new EventEmitter(); + +interface QueryRequest extends Request { + body: { + q: string; + budget?: number; + maxBadAttempt?: number; + }; +} + +function buildMdFromAnswer(answer: AnswerAction) { + let refStr = ''; + if (answer.references?.length > 0) { + refStr = ` + +## References +${answer.references.map((ref, i) => ` +${i + 1}. [${ref.exactQuote}](${ref.url})`).join('')}`; + } + return `${answer.answer.replace(/\(REF_(\d+)\)/g, (_, num) => `[^${num}]`)}${refStr}`; +} + + +// Modified streamTextWordByWord function +async function* streamTextWordByWord(text: string, streamingState: StreamingState) { + const words = text.split(/(\s+)/); + for (const word of words) { + if (streamingState.currentlyStreaming) { + const delay = Math.floor(Math.random() * 100); + await new Promise(resolve => setTimeout(resolve, delay)); + yield word; + } else { + // If streaming was interrupted, yield all remaining words at once + const remainingWords = words.slice(words.indexOf(word)).join(''); + yield remainingWords; + return; + } + } +} + +// Helper function to emit remaining content immediately +async function emitRemainingContent( + res: Response, + requestId: string, + model: string, + content: string +) { + if (!content) return; + + const chunk: ChatCompletionChunk = { + id: requestId, + object: 'chat.completion.chunk', + created: Math.floor(Date.now() / 1000), + model: model, + system_fingerprint: 'fp_' + requestId, + choices: [{ + index: 0, + delta: {content}, + logprobs: null, + finish_reason: null + }] + }; + res.write(`data: ${JSON.stringify(chunk)}\n\n`); +} + +interface StreamingState { + currentlyStreaming: boolean; + currentGenerator: AsyncGenerator | null; + remainingContent: string; +} + +async function completeCurrentStreaming( + streamingState: StreamingState, + res: Response, + requestId: string, + model: string +) { + if (streamingState.currentlyStreaming && streamingState.remainingContent) { + // Force completion of current streaming + await emitRemainingContent( + res, + requestId, + model, + streamingState.remainingContent + ); + // Reset streaming state + streamingState.currentlyStreaming = false; + streamingState.remainingContent = ''; + streamingState.currentGenerator = null; + } +} + +// OpenAI-compatible chat completions endpoint +// Models API endpoints +app.get('/v1/models', (async (_req: Request, res: Response) => { + const models: Model[] = [{ + id: 'jina-deepsearch-v1', + object: 'model', + created: 1686935002, + owned_by: 'jina-ai' + }]; + + res.json({ + object: 'list', + data: models + }); +}) as RequestHandler); + +app.get('/v1/models/:model', (async (req: Request, res: Response) => { + const modelId = req.params.model; + + if (modelId === 'jina-deepsearch-v1') { + res.json({ + id: 'jina-deepsearch-v1', + object: 'model', + created: 1686935002, + owned_by: 'jina-ai' + }); + } else { + res.status(404).json({ + error: { + message: `Model '${modelId}' not found`, + type: 'invalid_request_error', + param: null, + code: 'model_not_found' + } + }); + } +}) as RequestHandler); + +if (secret) { + // Check authentication only if secret is set + app.use((req, res, next) => { + const authHeader = req.headers.authorization; + if (!authHeader || !authHeader.startsWith('Bearer ') || authHeader.split(' ')[1] !== secret) { + console.log('[chat/completions] Unauthorized request'); + res.status(401).json({ error: 'Unauthorized' }); + return; + } + + return next(); + }); +} + +app.post('/v1/chat/completions', (async (req: Request, res: Response) => { + // Check authentication only if secret is set + if (secret) { + const authHeader = req.headers.authorization; + if (!authHeader || !authHeader.startsWith('Bearer ') || authHeader.split(' ')[1] !== secret) { + console.log('[chat/completions] Unauthorized request'); + res.status(401).json({error: 'Unauthorized'}); + return; + } + } + + // Log request details (excluding sensitive data) + console.log('[chat/completions] Request:', { + model: req.body.model, + stream: req.body.stream, + messageCount: req.body.messages?.length, + hasAuth: !!req.headers.authorization, + requestId: Date.now().toString() + }); + + const body = req.body as ChatCompletionRequest; + if (!body.messages?.length) { + return res.status(400).json({error: 'Messages array is required and must not be empty'}); + } + const lastMessage = body.messages[body.messages.length - 1]; + if (lastMessage.role !== 'user') { + return res.status(400).json({error: 'Last message must be from user'}); + } + + const requestId = Date.now().toString(); + const context: TrackerContext = { + tokenTracker: new TokenTracker(), + actionTracker: new ActionTracker() + }; + + // Track prompt tokens for the initial message + // Use Vercel's token counting convention - 1 token per message + const messageTokens = body.messages.length; + context.tokenTracker.trackUsage('agent', messageTokens, TOKEN_CATEGORIES.PROMPT); + + // Add this inside the chat completions endpoint, before setting up the action listener + const streamingState: StreamingState = { + currentlyStreaming: false, + currentGenerator: null, + remainingContent: '' + }; + + if (body.stream) { + res.setHeader('Content-Type', 'text/event-stream'); + res.setHeader('Cache-Control', 'no-cache'); + res.setHeader('Connection', 'keep-alive'); + + + // Send initial chunk with opening think tag + const initialChunk: ChatCompletionChunk = { + id: requestId, + object: 'chat.completion.chunk', + created: Math.floor(Date.now() / 1000), + model: body.model, + system_fingerprint: 'fp_' + requestId, + choices: [{ + index: 0, + delta: {role: 'assistant', content: ''}, + logprobs: null, + finish_reason: null + }] + }; + res.write(`data: ${JSON.stringify(initialChunk)}\n\n`); + + // Set up progress listener with cleanup + const actionListener = async (action: any) => { + if (action.thisStep.think) { + // Complete any ongoing streaming first + await completeCurrentStreaming(streamingState, res, requestId, body.model); + + // Start new streaming session + streamingState.currentlyStreaming = true; + streamingState.remainingContent = action.thisStep.think; + + try { + for await (const word of streamTextWordByWord(action.thisStep.think, streamingState)) { + if (!streamingState.currentlyStreaming) { + break; + } + + // Update remaining content + streamingState.remainingContent = streamingState.remainingContent.slice(word.length); + + const chunk: ChatCompletionChunk = { + id: requestId, + object: 'chat.completion.chunk', + created: Math.floor(Date.now() / 1000), + model: body.model, + system_fingerprint: 'fp_' + requestId, + choices: [{ + index: 0, + delta: {content: word}, + logprobs: null, + finish_reason: null + }] + }; + res.write(`data: ${JSON.stringify(chunk)}\n\n`); + } + + // Only add newline if this streaming completed normally + if (streamingState.currentlyStreaming) { + const newlineChunk: ChatCompletionChunk = { + id: requestId, + object: 'chat.completion.chunk', + created: Math.floor(Date.now() / 1000), + model: body.model, + system_fingerprint: 'fp_' + requestId, + choices: [{ + index: 0, + delta: {content: '\n'}, + logprobs: null, + finish_reason: null + }] + }; + res.write(`data: ${JSON.stringify(newlineChunk)}\n\n`); + } + } catch (error) { + console.error('Error in streaming:', error); + await completeCurrentStreaming(streamingState, res, requestId, body.model); + } + } + }; + context.actionTracker.on('action', actionListener); + + // Make sure to update the cleanup code + res.on('finish', () => { + streamingState.currentlyStreaming = false; + streamingState.currentGenerator = null; + streamingState.remainingContent = ''; + context.actionTracker.removeListener('action', actionListener); + }); + } + + try { + // Track initial query tokens - already tracked above + // const queryTokens = Buffer.byteLength(lastMessage.content, 'utf-8'); + // context.tokenTracker.trackUsage('agent', queryTokens, 'prompt'); + + let result; + try { + ({result} = await getResponse(lastMessage.content, undefined, undefined, context)); + } catch (error: any) { + // If deduplication fails, retry without it + if (error?.response?.status === 402) { + // If deduplication fails, retry with maxBadAttempt=3 to skip dedup + ({result} = await getResponse(lastMessage.content, undefined, 3, context)); + } else { + throw error; + } + } + + // Track tokens based on action type + if (result.action === 'answer') { + // Track accepted prediction tokens for the final answer using Vercel's convention + const answerTokens = 1; // Default to 1 token per answer + context.tokenTracker.trackUsage('evaluator', answerTokens, TOKEN_CATEGORIES.ACCEPTED); + } else { + // Track rejected prediction tokens for non-answer responses + const rejectedTokens = 1; // Default to 1 token per rejected response + context.tokenTracker.trackUsage('evaluator', rejectedTokens, TOKEN_CATEGORIES.REJECTED); + } + + if (body.stream) { + // Complete any ongoing streaming before sending final answer + await completeCurrentStreaming(streamingState, res, requestId, body.model); + + // Send closing think tag + const closeThinkChunk: ChatCompletionChunk = { + id: requestId, + object: 'chat.completion.chunk', + created: Math.floor(Date.now() / 1000), + model: body.model, + system_fingerprint: 'fp_' + requestId, + choices: [{ + index: 0, + delta: {content: `\n\n`}, + logprobs: null, + finish_reason: null + }] + }; + res.write(`data: ${JSON.stringify(closeThinkChunk)}\n\n`); + + // Send final answer as separate chunk + const answerChunk: ChatCompletionChunk = { + id: requestId, + object: 'chat.completion.chunk', + created: Math.floor(Date.now() / 1000), + model: body.model, + system_fingerprint: 'fp_' + requestId, + choices: [{ + index: 0, + delta: {content: result.action === 'answer' ? buildMdFromAnswer(result) : result.think}, + logprobs: null, + finish_reason: 'stop' + }] + }; + res.write(`data: ${JSON.stringify(answerChunk)}\n\n`); + res.end(); + } else { + const usage = context.tokenTracker.getUsageDetails(); + const response: ChatCompletionResponse = { + id: requestId, + object: 'chat.completion', + created: Math.floor(Date.now() / 1000), + model: body.model, + system_fingerprint: 'fp_' + requestId, + choices: [{ + index: 0, + message: { + role: 'assistant', + content: result.action === 'answer' ? buildMdFromAnswer(result) : result.think + }, + logprobs: null, + finish_reason: 'stop' + }], + usage + }; + + // Log final response (excluding full content for brevity) + console.log('[chat/completions] Response:', { + id: response.id, + status: 200, + contentLength: response.choices[0].message.content.length, + usage: response.usage + }); + + res.json(response); + } + } catch (error: any) { + // Log error details + console.error('[chat/completions] Error:', { + message: error?.message || 'An error occurred', + stack: error?.stack, + type: error?.constructor?.name, + requestId + }); + + // Track error as rejected tokens with Vercel token counting + const errorMessage = error?.message || 'An error occurred'; + // Default to 1 token for errors as per Vercel AI SDK convention + const errorTokens = 1; + context.tokenTracker.trackUsage('evaluator', errorTokens, TOKEN_CATEGORIES.REJECTED); + + // Clean up event listeners + context.actionTracker.removeAllListeners('action'); + + // Get token usage in OpenAI API format + const usage = context.tokenTracker.getUsageDetails(); + + if (body.stream && res.headersSent) { + // For streaming responses that have already started, send error as a chunk + // First send closing think tag if we're in the middle of thinking + const closeThinkChunk: ChatCompletionChunk = { + id: requestId, + object: 'chat.completion.chunk', + created: Math.floor(Date.now() / 1000), + model: body.model, + system_fingerprint: 'fp_' + requestId, + choices: [{ + index: 0, + delta: {content: ''}, + logprobs: null, + finish_reason: null + }] + }; + res.write(`data: ${JSON.stringify(closeThinkChunk)}\n\n`); + + // Track error token and send error message + context.tokenTracker.trackUsage('evaluator', 1, TOKEN_CATEGORIES.REJECTED); + const errorChunk: ChatCompletionChunk = { + id: requestId, + object: 'chat.completion.chunk', + created: Math.floor(Date.now() / 1000), + model: body.model, + system_fingerprint: 'fp_' + requestId, + choices: [{ + index: 0, + delta: {content: errorMessage}, + logprobs: null, + finish_reason: 'stop' + }] + }; + res.write(`data: ${JSON.stringify(errorChunk)}\n\n`); + res.end(); + } else { + // For non-streaming or not-yet-started responses, send error as JSON + const response: ChatCompletionResponse = { + id: requestId, + object: 'chat.completion', + created: Math.floor(Date.now() / 1000), + model: body.model, + system_fingerprint: 'fp_' + requestId, + choices: [{ + index: 0, + message: { + role: 'assistant', + content: `Error: ${errorMessage}` + }, + logprobs: null, + finish_reason: 'stop' + }], + usage + }; + res.json(response); + } + } +}) as RequestHandler); + +interface StreamResponse extends Response { + write: (chunk: string) => boolean; +} + +function createProgressEmitter(requestId: string, budget: number | undefined, context: TrackerContext) { + return () => { + const state = context.actionTracker.getState(); + const budgetInfo = { + used: context.tokenTracker.getTotalUsage(), + total: budget || 1_000_000, + percentage: ((context.tokenTracker.getTotalUsage() / (budget || 1_000_000)) * 100).toFixed(2) + }; + + eventEmitter.emit(`progress-${requestId}`, { + type: 'progress', + data: {...state.thisStep, totalStep: state.totalStep}, + step: state.totalStep, + budget: budgetInfo, + trackers: { + tokenUsage: context.tokenTracker.getTotalUsage(), + actionState: context.actionTracker.getState() + } + }); + }; +} + +function cleanup(requestId: string) { + const context = trackers.get(requestId); + if (context) { + context.actionTracker.removeAllListeners(); + context.tokenTracker.removeAllListeners(); + trackers.delete(requestId); + } +} + +function emitTrackerUpdate(requestId: string, context: TrackerContext) { + const trackerData = { + tokenUsage: context.tokenTracker.getTotalUsage(), + tokenBreakdown: context.tokenTracker.getUsageBreakdown(), + actionState: context.actionTracker.getState().thisStep, + step: context.actionTracker.getState().totalStep, + badAttempts: context.actionTracker.getState().badAttempts, + gaps: context.actionTracker.getState().gaps + }; + + eventEmitter.emit(`progress-${requestId}`, { + type: 'progress', + trackers: trackerData + }); +} + +// Store the trackers for each request +const trackers = new Map(); + +app.post('/api/v1/query', (async (req: QueryRequest, res: Response) => { + const {q, budget, maxBadAttempt} = req.body; + if (!q) { + return res.status(400).json({error: 'Query (q) is required'}); + } + + const requestId = Date.now().toString(); + + // Create new trackers for this request + const context: TrackerContext = { + tokenTracker: new TokenTracker(), + actionTracker: new ActionTracker() + }; + trackers.set(requestId, context); + + // Set up listeners immediately for both trackers + context.actionTracker.on('action', () => emitTrackerUpdate(requestId, context)); + // context.tokenTracker.on('usage', () => emitTrackerUpdate(requestId, context)); + + res.json({requestId}); + + try { + const {result} = await getResponse(q, budget, maxBadAttempt, context); + const emitProgress = createProgressEmitter(requestId, budget, context); + context.actionTracker.on('action', emitProgress); + await storeTaskResult(requestId, result); + eventEmitter.emit(`progress-${requestId}`, { + type: 'answer', + data: result, + trackers: { + tokenUsage: context.tokenTracker.getTotalUsage(), + actionState: context.actionTracker.getState() + } + }); + cleanup(requestId); + } catch (error: any) { + eventEmitter.emit(`progress-${requestId}`, { + type: 'error', + data: error?.message || 'Unknown error', + status: 500, + trackers: { + tokenUsage: context.tokenTracker.getTotalUsage(), + actionState: context.actionTracker.getState() + } + }); + cleanup(requestId); + } +}) as RequestHandler); + +app.get('/api/v1/stream/:requestId', (async (req: Request, res: StreamResponse) => { + const requestId = req.params.requestId; + const context = trackers.get(requestId); + + res.setHeader('Content-Type', 'text/event-stream'); + res.setHeader('Cache-Control', 'no-cache'); + res.setHeader('Connection', 'keep-alive'); + + const listener = (data: StreamMessage) => { + // The trackers are now included in all event types + // We don't need to add them here as they're already part of the data + res.write(`data: ${JSON.stringify(data)}\n\n`); + }; + + eventEmitter.on(`progress-${requestId}`, listener); + + // Handle client disconnection + req.on('close', () => { + eventEmitter.removeListener(`progress-${requestId}`, listener); + }); + + // Send initial connection confirmation with tracker state + const initialData = { + type: 'connected', + requestId, + trackers: context ? { + tokenUsage: context.tokenTracker.getTotalUsage(), + actionState: context.actionTracker.getState() + } : null + }; + res.write(`data: ${JSON.stringify(initialData)}\n\n`); +}) as RequestHandler); + +async function storeTaskResult(requestId: string, result: StepAction) { + try { + const taskDir = path.join(process.cwd(), 'tasks'); + await fs.mkdir(taskDir, {recursive: true}); + await fs.writeFile( + path.join(taskDir, `${requestId}.json`), + JSON.stringify(result, null, 2) + ); + } catch (error) { + console.error('Task storage failed:', error); + throw new Error('Failed to store task result'); + } +} + +app.get('/api/v1/task/:requestId', (async (req: Request, res: Response) => { + const requestId = req.params.requestId; + try { + const taskPath = path.join(process.cwd(), 'tasks', `${requestId}.json`); + const taskData = await fs.readFile(taskPath, 'utf-8'); + res.json(JSON.parse(taskData)); + } catch (error) { + res.status(404).json({error: 'Task not found'}); + } +}) as RequestHandler); + +export default app; diff --git a/src/server.ts b/src/server.ts index a4e9c66..84ea43d 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,637 +1,7 @@ -import express, {Request, Response, RequestHandler} from 'express'; -import cors from 'cors'; -import {EventEmitter} from 'events'; -import {getResponse} from './agent'; -import { - StepAction, - StreamMessage, - TrackerContext, - ChatCompletionRequest, - ChatCompletionResponse, - ChatCompletionChunk, - AnswerAction, - TOKEN_CATEGORIES, - Model -} from './types'; -import fs from 'fs/promises'; -import path from 'path'; -import {TokenTracker} from "./utils/token-tracker"; -import {ActionTracker} from "./utils/action-tracker"; +import app from "./app"; -const app = express(); const port = process.env.PORT || 3000; -// Get secret from command line args for optional authentication -const secret = process.argv.find(arg => arg.startsWith('--secret='))?.split('=')[1]; - -app.use(cors()); -app.use(express.json()); - -const eventEmitter = new EventEmitter(); - -interface QueryRequest extends Request { - body: { - q: string; - budget?: number; - maxBadAttempt?: number; - }; -} - -function buildMdFromAnswer(answer: AnswerAction) { - let refStr = ''; - if (answer.references?.length > 0) { - refStr = ` - -## References -${answer.references.map((ref, i) => ` -${i + 1}. [${ref.exactQuote}](${ref.url})`).join('')}`; - } - return `${answer.answer.replace(/\(REF_(\d+)\)/g, (_, num) => `[^${num}]`)}${refStr}`; -} - - -// Modified streamTextWordByWord function -async function* streamTextWordByWord(text: string, streamingState: StreamingState) { - const words = text.split(/(\s+)/); - for (const word of words) { - if (streamingState.currentlyStreaming) { - const delay = Math.floor(Math.random() * 100); - await new Promise(resolve => setTimeout(resolve, delay)); - yield word; - } else { - // If streaming was interrupted, yield all remaining words at once - const remainingWords = words.slice(words.indexOf(word)).join(''); - yield remainingWords; - return; - } - } -} - -// Helper function to emit remaining content immediately -async function emitRemainingContent( - res: Response, - requestId: string, - model: string, - content: string -) { - if (!content) return; - - const chunk: ChatCompletionChunk = { - id: requestId, - object: 'chat.completion.chunk', - created: Math.floor(Date.now() / 1000), - model: model, - system_fingerprint: 'fp_' + requestId, - choices: [{ - index: 0, - delta: {content}, - logprobs: null, - finish_reason: null - }] - }; - res.write(`data: ${JSON.stringify(chunk)}\n\n`); -} - -interface StreamingState { - currentlyStreaming: boolean; - currentGenerator: AsyncGenerator | null; - remainingContent: string; -} - -async function completeCurrentStreaming( - streamingState: StreamingState, - res: Response, - requestId: string, - model: string -) { - if (streamingState.currentlyStreaming && streamingState.remainingContent) { - // Force completion of current streaming - await emitRemainingContent( - res, - requestId, - model, - streamingState.remainingContent - ); - // Reset streaming state - streamingState.currentlyStreaming = false; - streamingState.remainingContent = ''; - streamingState.currentGenerator = null; - } -} - -// OpenAI-compatible chat completions endpoint -// Models API endpoints -app.get('/v1/models', (async (_req: Request, res: Response) => { - const models: Model[] = [{ - id: 'jina-deepsearch-v1', - object: 'model', - created: 1686935002, - owned_by: 'jina-ai' - }]; - - res.json({ - object: 'list', - data: models - }); -}) as RequestHandler); - -app.get('/v1/models/:model', (async (req: Request, res: Response) => { - const modelId = req.params.model; - - if (modelId === 'jina-deepsearch-v1') { - res.json({ - id: 'jina-deepsearch-v1', - object: 'model', - created: 1686935002, - owned_by: 'jina-ai' - }); - } else { - res.status(404).json({ - error: { - message: `Model '${modelId}' not found`, - type: 'invalid_request_error', - param: null, - code: 'model_not_found' - } - }); - } -}) as RequestHandler); - - -app.post('/v1/chat/completions', (async (req: Request, res: Response) => { - // Check authentication only if secret is set - if (secret) { - const authHeader = req.headers.authorization; - if (!authHeader || !authHeader.startsWith('Bearer ') || authHeader.split(' ')[1] !== secret) { - console.log('[chat/completions] Unauthorized request'); - res.status(401).json({error: 'Unauthorized'}); - return; - } - } - - // Log request details (excluding sensitive data) - console.log('[chat/completions] Request:', { - model: req.body.model, - stream: req.body.stream, - messageCount: req.body.messages?.length, - hasAuth: !!req.headers.authorization, - requestId: Date.now().toString() - }); - - const body = req.body as ChatCompletionRequest; - if (!body.messages?.length) { - return res.status(400).json({error: 'Messages array is required and must not be empty'}); - } - const lastMessage = body.messages[body.messages.length - 1]; - if (lastMessage.role !== 'user') { - return res.status(400).json({error: 'Last message must be from user'}); - } - - const requestId = Date.now().toString(); - const context: TrackerContext = { - tokenTracker: new TokenTracker(), - actionTracker: new ActionTracker() - }; - - // Track prompt tokens for the initial message - // Use Vercel's token counting convention - 1 token per message - const messageTokens = body.messages.length; - context.tokenTracker.trackUsage('agent', messageTokens, TOKEN_CATEGORIES.PROMPT); - - // Add this inside the chat completions endpoint, before setting up the action listener - const streamingState: StreamingState = { - currentlyStreaming: false, - currentGenerator: null, - remainingContent: '' - }; - - if (body.stream) { - res.setHeader('Content-Type', 'text/event-stream'); - res.setHeader('Cache-Control', 'no-cache'); - res.setHeader('Connection', 'keep-alive'); - - - // Send initial chunk with opening think tag - const initialChunk: ChatCompletionChunk = { - id: requestId, - object: 'chat.completion.chunk', - created: Math.floor(Date.now() / 1000), - model: body.model, - system_fingerprint: 'fp_' + requestId, - choices: [{ - index: 0, - delta: {role: 'assistant', content: ''}, - logprobs: null, - finish_reason: null - }] - }; - res.write(`data: ${JSON.stringify(initialChunk)}\n\n`); - - // Set up progress listener with cleanup - const actionListener = async (action: any) => { - if (action.thisStep.think) { - // Complete any ongoing streaming first - await completeCurrentStreaming(streamingState, res, requestId, body.model); - - // Start new streaming session - streamingState.currentlyStreaming = true; - streamingState.remainingContent = action.thisStep.think; - - try { - for await (const word of streamTextWordByWord(action.thisStep.think, streamingState)) { - if (!streamingState.currentlyStreaming) { - break; - } - - // Update remaining content - streamingState.remainingContent = streamingState.remainingContent.slice(word.length); - - const chunk: ChatCompletionChunk = { - id: requestId, - object: 'chat.completion.chunk', - created: Math.floor(Date.now() / 1000), - model: body.model, - system_fingerprint: 'fp_' + requestId, - choices: [{ - index: 0, - delta: {content: word}, - logprobs: null, - finish_reason: null - }] - }; - res.write(`data: ${JSON.stringify(chunk)}\n\n`); - } - - // Only add newline if this streaming completed normally - if (streamingState.currentlyStreaming) { - const newlineChunk: ChatCompletionChunk = { - id: requestId, - object: 'chat.completion.chunk', - created: Math.floor(Date.now() / 1000), - model: body.model, - system_fingerprint: 'fp_' + requestId, - choices: [{ - index: 0, - delta: {content: '\n'}, - logprobs: null, - finish_reason: null - }] - }; - res.write(`data: ${JSON.stringify(newlineChunk)}\n\n`); - } - } catch (error) { - console.error('Error in streaming:', error); - await completeCurrentStreaming(streamingState, res, requestId, body.model); - } - } - }; - context.actionTracker.on('action', actionListener); - - // Make sure to update the cleanup code - res.on('finish', () => { - streamingState.currentlyStreaming = false; - streamingState.currentGenerator = null; - streamingState.remainingContent = ''; - context.actionTracker.removeListener('action', actionListener); - }); - } - - try { - // Track initial query tokens - already tracked above - // const queryTokens = Buffer.byteLength(lastMessage.content, 'utf-8'); - // context.tokenTracker.trackUsage('agent', queryTokens, 'prompt'); - - let result; - try { - ({result} = await getResponse(lastMessage.content, undefined, undefined, context)); - } catch (error: any) { - // If deduplication fails, retry without it - if (error?.response?.status === 402) { - // If deduplication fails, retry with maxBadAttempt=3 to skip dedup - ({result} = await getResponse(lastMessage.content, undefined, 3, context)); - } else { - throw error; - } - } - - // Track tokens based on action type - if (result.action === 'answer') { - // Track accepted prediction tokens for the final answer using Vercel's convention - const answerTokens = 1; // Default to 1 token per answer - context.tokenTracker.trackUsage('evaluator', answerTokens, TOKEN_CATEGORIES.ACCEPTED); - } else { - // Track rejected prediction tokens for non-answer responses - const rejectedTokens = 1; // Default to 1 token per rejected response - context.tokenTracker.trackUsage('evaluator', rejectedTokens, TOKEN_CATEGORIES.REJECTED); - } - - if (body.stream) { - // Complete any ongoing streaming before sending final answer - await completeCurrentStreaming(streamingState, res, requestId, body.model); - - // Send closing think tag - const closeThinkChunk: ChatCompletionChunk = { - id: requestId, - object: 'chat.completion.chunk', - created: Math.floor(Date.now() / 1000), - model: body.model, - system_fingerprint: 'fp_' + requestId, - choices: [{ - index: 0, - delta: {content: `\n\n`}, - logprobs: null, - finish_reason: null - }] - }; - res.write(`data: ${JSON.stringify(closeThinkChunk)}\n\n`); - - // Send final answer as separate chunk - const answerChunk: ChatCompletionChunk = { - id: requestId, - object: 'chat.completion.chunk', - created: Math.floor(Date.now() / 1000), - model: body.model, - system_fingerprint: 'fp_' + requestId, - choices: [{ - index: 0, - delta: {content: result.action === 'answer' ? buildMdFromAnswer(result) : result.think}, - logprobs: null, - finish_reason: 'stop' - }] - }; - res.write(`data: ${JSON.stringify(answerChunk)}\n\n`); - res.end(); - } else { - const usage = context.tokenTracker.getUsageDetails(); - const response: ChatCompletionResponse = { - id: requestId, - object: 'chat.completion', - created: Math.floor(Date.now() / 1000), - model: body.model, - system_fingerprint: 'fp_' + requestId, - choices: [{ - index: 0, - message: { - role: 'assistant', - content: result.action === 'answer' ? buildMdFromAnswer(result) : result.think - }, - logprobs: null, - finish_reason: 'stop' - }], - usage - }; - - // Log final response (excluding full content for brevity) - console.log('[chat/completions] Response:', { - id: response.id, - status: 200, - contentLength: response.choices[0].message.content.length, - usage: response.usage - }); - - res.json(response); - } - } catch (error: any) { - // Log error details - console.error('[chat/completions] Error:', { - message: error?.message || 'An error occurred', - stack: error?.stack, - type: error?.constructor?.name, - requestId - }); - - // Track error as rejected tokens with Vercel token counting - const errorMessage = error?.message || 'An error occurred'; - // Default to 1 token for errors as per Vercel AI SDK convention - const errorTokens = 1; - context.tokenTracker.trackUsage('evaluator', errorTokens, TOKEN_CATEGORIES.REJECTED); - - // Clean up event listeners - context.actionTracker.removeAllListeners('action'); - - // Get token usage in OpenAI API format - const usage = context.tokenTracker.getUsageDetails(); - - if (body.stream && res.headersSent) { - // For streaming responses that have already started, send error as a chunk - // First send closing think tag if we're in the middle of thinking - const closeThinkChunk: ChatCompletionChunk = { - id: requestId, - object: 'chat.completion.chunk', - created: Math.floor(Date.now() / 1000), - model: body.model, - system_fingerprint: 'fp_' + requestId, - choices: [{ - index: 0, - delta: {content: ''}, - logprobs: null, - finish_reason: null - }] - }; - res.write(`data: ${JSON.stringify(closeThinkChunk)}\n\n`); - - // Track error token and send error message - context.tokenTracker.trackUsage('evaluator', 1, TOKEN_CATEGORIES.REJECTED); - const errorChunk: ChatCompletionChunk = { - id: requestId, - object: 'chat.completion.chunk', - created: Math.floor(Date.now() / 1000), - model: body.model, - system_fingerprint: 'fp_' + requestId, - choices: [{ - index: 0, - delta: {content: errorMessage}, - logprobs: null, - finish_reason: 'stop' - }] - }; - res.write(`data: ${JSON.stringify(errorChunk)}\n\n`); - res.end(); - } else { - // For non-streaming or not-yet-started responses, send error as JSON - const response: ChatCompletionResponse = { - id: requestId, - object: 'chat.completion', - created: Math.floor(Date.now() / 1000), - model: body.model, - system_fingerprint: 'fp_' + requestId, - choices: [{ - index: 0, - message: { - role: 'assistant', - content: `Error: ${errorMessage}` - }, - logprobs: null, - finish_reason: 'stop' - }], - usage - }; - res.json(response); - } - } -}) as RequestHandler); - -interface StreamResponse extends Response { - write: (chunk: string) => boolean; -} - -function createProgressEmitter(requestId: string, budget: number | undefined, context: TrackerContext) { - return () => { - const state = context.actionTracker.getState(); - const budgetInfo = { - used: context.tokenTracker.getTotalUsage(), - total: budget || 1_000_000, - percentage: ((context.tokenTracker.getTotalUsage() / (budget || 1_000_000)) * 100).toFixed(2) - }; - - eventEmitter.emit(`progress-${requestId}`, { - type: 'progress', - data: {...state.thisStep, totalStep: state.totalStep}, - step: state.totalStep, - budget: budgetInfo, - trackers: { - tokenUsage: context.tokenTracker.getTotalUsage(), - actionState: context.actionTracker.getState() - } - }); - }; -} - -function cleanup(requestId: string) { - const context = trackers.get(requestId); - if (context) { - context.actionTracker.removeAllListeners(); - context.tokenTracker.removeAllListeners(); - trackers.delete(requestId); - } -} - -function emitTrackerUpdate(requestId: string, context: TrackerContext) { - const trackerData = { - tokenUsage: context.tokenTracker.getTotalUsage(), - tokenBreakdown: context.tokenTracker.getUsageBreakdown(), - actionState: context.actionTracker.getState().thisStep, - step: context.actionTracker.getState().totalStep, - badAttempts: context.actionTracker.getState().badAttempts, - gaps: context.actionTracker.getState().gaps - }; - - eventEmitter.emit(`progress-${requestId}`, { - type: 'progress', - trackers: trackerData - }); -} - -// Store the trackers for each request -const trackers = new Map(); - -app.post('/api/v1/query', (async (req: QueryRequest, res: Response) => { - const {q, budget, maxBadAttempt} = req.body; - if (!q) { - return res.status(400).json({error: 'Query (q) is required'}); - } - - const requestId = Date.now().toString(); - - // Create new trackers for this request - const context: TrackerContext = { - tokenTracker: new TokenTracker(), - actionTracker: new ActionTracker() - }; - trackers.set(requestId, context); - - // Set up listeners immediately for both trackers - context.actionTracker.on('action', () => emitTrackerUpdate(requestId, context)); - // context.tokenTracker.on('usage', () => emitTrackerUpdate(requestId, context)); - - res.json({requestId}); - - try { - const {result} = await getResponse(q, budget, maxBadAttempt, context); - const emitProgress = createProgressEmitter(requestId, budget, context); - context.actionTracker.on('action', emitProgress); - await storeTaskResult(requestId, result); - eventEmitter.emit(`progress-${requestId}`, { - type: 'answer', - data: result, - trackers: { - tokenUsage: context.tokenTracker.getTotalUsage(), - actionState: context.actionTracker.getState() - } - }); - cleanup(requestId); - } catch (error: any) { - eventEmitter.emit(`progress-${requestId}`, { - type: 'error', - data: error?.message || 'Unknown error', - status: 500, - trackers: { - tokenUsage: context.tokenTracker.getTotalUsage(), - actionState: context.actionTracker.getState() - } - }); - cleanup(requestId); - } -}) as RequestHandler); - -app.get('/api/v1/stream/:requestId', (async (req: Request, res: StreamResponse) => { - const requestId = req.params.requestId; - const context = trackers.get(requestId); - - res.setHeader('Content-Type', 'text/event-stream'); - res.setHeader('Cache-Control', 'no-cache'); - res.setHeader('Connection', 'keep-alive'); - - const listener = (data: StreamMessage) => { - // The trackers are now included in all event types - // We don't need to add them here as they're already part of the data - res.write(`data: ${JSON.stringify(data)}\n\n`); - }; - - eventEmitter.on(`progress-${requestId}`, listener); - - // Handle client disconnection - req.on('close', () => { - eventEmitter.removeListener(`progress-${requestId}`, listener); - }); - - // Send initial connection confirmation with tracker state - const initialData = { - type: 'connected', - requestId, - trackers: context ? { - tokenUsage: context.tokenTracker.getTotalUsage(), - actionState: context.actionTracker.getState() - } : null - }; - res.write(`data: ${JSON.stringify(initialData)}\n\n`); -}) as RequestHandler); - -async function storeTaskResult(requestId: string, result: StepAction) { - try { - const taskDir = path.join(process.cwd(), 'tasks'); - await fs.mkdir(taskDir, {recursive: true}); - await fs.writeFile( - path.join(taskDir, `${requestId}.json`), - JSON.stringify(result, null, 2) - ); - } catch (error) { - console.error('Task storage failed:', error); - throw new Error('Failed to store task result'); - } -} - -app.get('/api/v1/task/:requestId', (async (req: Request, res: Response) => { - const requestId = req.params.requestId; - try { - const taskPath = path.join(process.cwd(), 'tasks', `${requestId}.json`); - const taskData = await fs.readFile(taskPath, 'utf-8'); - res.json(JSON.parse(taskData)); - } catch (error) { - res.status(404).json({error: 'Task not found'}); - } -}) as RequestHandler); - // Export server startup function for better testing export function startServer() { return app.listen(port, () => { @@ -642,6 +12,4 @@ export function startServer() { // Start server if running directly if (process.env.NODE_ENV !== 'test') { startServer(); -} - -export default app; +} \ No newline at end of file diff --git a/src/utils/token-tracker.ts b/src/utils/token-tracker.ts index 6e0eec0..5168a85 100644 --- a/src/utils/token-tracker.ts +++ b/src/utils/token-tracker.ts @@ -9,6 +9,16 @@ export class TokenTracker extends EventEmitter { constructor(budget?: number) { super(); this.budget = budget; + + if ('asyncLocalContext' in process) { + const asyncLocalContext = process.asyncLocalContext as any; + this.on('usage', () => { + if (asyncLocalContext.available()) { + asyncLocalContext.ctx.chargeAmount = this.getTotalUsage(); + } + }); + + } } trackUsage(tool: string, tokens: number, category?: TokenCategory) { @@ -53,9 +63,9 @@ export class TokenTracker extends EventEmitter { }, {} as Record); const prompt_tokens = categoryBreakdown.prompt || 0; - const completion_tokens = - (categoryBreakdown.reasoning || 0) + - (categoryBreakdown.accepted || 0) + + const completion_tokens = + (categoryBreakdown.reasoning || 0) + + (categoryBreakdown.accepted || 0) + (categoryBreakdown.rejected || 0); return { diff --git a/tsconfig.json b/tsconfig.json index 6b6d13c..def3627 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,14 +1,18 @@ { "compilerOptions": { "target": "ES2020", - "module": "commonjs", + "module": "node16", "outDir": "./dist", "rootDir": "./src", + "sourceMap": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "strict": true, - "resolveJsonModule": true, - "moduleResolution": "node" - } + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "resolveJsonModule": true + }, + "include": ["src/**/*"], + "exclude": ["jina-ai/**/*", "**/__tests__/**/*"], }